Как исправить ошибки "Переменная не определена" и "Поле недоступно для записи" при работе с COM-соединением в 1С?

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

При интеграции различных конфигураций 1С, особенно когда одна из них — старая и сильно доработанная (например, УПП на платформе 8.1), часто возникают специфические ошибки. Разберем ситуацию, когда из современной конфигурации ЗУП через COM-соединение необходимо создавать и изменять документы в старой УПП. Проанализируем две типичные проблемы, с которыми можно столкнуться, и найдем для них решение.

Проблема 1: Ошибка "Переменная не определена" при вызове общего модуля

Представим ситуацию: мы устанавливаем COM-соединение из ЗУП к базе УПП и пытаемся программно создать документ, например, "Зарплата к выплате организации". Соединение успешно, но в момент создания объекта возникает ошибка:

Произошла исключительная ситуация (1C:Enterprise 8.2.19.106): {Документ.ЗарплатаКВыплатеОрганизации.МодульОбъекта(624,54)}: Переменная не определена (мРаботаСДиалогами)

При этом мРаботаСДиалогами — это существующий общий модуль в конфигурации УПП. Давайте выясним причину и найдем решение.

Анализ причины ошибки

Ключ к пониманию проблемы лежит в контексте выполнения кода. COM-соединение всегда работает в серверном (или внешнего соединения) контексте. Это означает, что в сеансе, который мы создали программно, отсутствует пользовательский интерфейс. Соответственно, любой код, предназначенный для интерактивной работы с пользователем (открытие форм, показ сообщений, вызов диалоговых окон), будет недоступен.

Модули, подобные мРаботаСДиалогами, как правило, содержат именно такой "клиентский" код. В старых конфигурациях, разработанных во времена преобладания толстого клиента, разработчики не всегда строго разделяли логику на клиентскую и серверную. В результате, в модуле объекта документа может находиться прямой вызов процедуры из клиентского общего модуля, что и приводит к ошибке в серверном контексте.

Способы решения

Существует два основных подхода к решению этой проблемы: быстрый, но локальный, и правильный, но более трудоемкий.

  1. Установка флага "Внешнее соединение" для общего модуля.

    Это самое простое и быстрое решение. Чтобы общий модуль стал "видимым" для сеанса внешнего соединения, необходимо в его свойствах установить соответствующий флаг.

    Пошаговая инструкция:

    1. Откройте конфигурацию УПП в режиме Конфигуратора.
    2. В дереве конфигурации найдите ветку "Общие" -> "Общие модули".
    3. Найдите проблемный модуль (в нашем случае мРаботаСДиалогами).
    4. Откройте его свойства (правой кнопкой мыши -> "Свойства" или через палитру свойств).
    5. Найдите флаг (галочку) "Внешнее соединение" и установите его.
    6. Сохраните и обновите конфигурацию базы данных.

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

  2. Рефакторинг кода с использованием директив препроцессора.

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

    Используются следующие директивы:

    • #Если Клиент Тогда ... #КонецЕсли — код внутри этого блока будет скомпилирован и выполнен только на клиенте.
    • #Если Сервер Тогда ... #КонецЕсли — код для выполнения на сервере.
    • #Если ВнешнееСоединение Тогда ... #КонецЕсли — код специально для сеансов внешнего соединения.

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

    Пример:

    Предположим, в коде модуля объекта есть такой фрагмент:

    
    // ... какая-то логика
    мРаботаСДиалогами.ПоказатьПредупреждениеПользователю("Документ создан!");
    // ... дальнейшая логика
    

    При выполнении через COM этот код вызовет ошибку. Чтобы ее избежать, нужно изменить код следующим образом:

    
    // ... какая-то логика
    #Если Клиент Тогда
        мРаботаСДиалогами.ПоказатьПредупреждениеПользователю("Документ создан!");
    #КонецЕсли
    // ... дальнейшая логика
    

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

Проблема 2: Ошибка "Поле объекта недоступно для записи (Проведен)"

Итак, мы решили первую проблему и смогли создать документ. Теперь возникает новая задача: найти существующий документ и изменить его, например, отменить проведение. Мы пишем код, который находит документ, и пытаемся изменить его свойство Проведен:


// ... код подключения ...
// Находим документ в базе УПП
НайденныйДок = БазаУПП.Документы.ЗарплатаКВыплатеОрганизации.НайтиПоНомеру("...");

Если НЕ НайденныйДок.Пустая() Тогда
    // Пытаемся отменить проведение
    НайденныйДок.Проведен = Ложь; // Здесь возникает ошибка!
КонецЕсли;

В результате выполнения этого кода мы получаем ошибку:

Поле объекта недоступно для записи (Проведен)

При этом, если открыть тот же документ в интерфейсе УПП и отменить проведение вручную, все работает. Разберемся, в чем дело.

Анализ причины: Ссылка vs. Объект

Эта ошибка — классический пример непонимания разницы между ссылкой на объект и самим объектом в 1С.

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

Правильный способ изменения документа

Чтобы корректно изменить документ, нужно выполнить следующую последовательность действий:

  1. Получить объект из ссылки.

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

  2. Внести изменения в объект.

    Теперь мы можем менять реквизиты, табличные части и т.д.

  3. Записать объект.

    Для отмены проведения нельзя просто присвоить Проведен = Ложь. Необходимо использовать метод Записать() со специальным параметром — РежимЗаписиДокумента.ОтменаПроведения. Этот режим гарантирует, что платформа корректно отменит проведение и удалит все движения документа по регистрам.

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

Посмотрим на исправленный код:


// ... код подключения ...
// Находим ссылку на документ в базе УПП
НайденнаяСсылка = БазаУПП.Документы.ЗарплатаКВыплатеОрганизации.НайтиПоНомеру("...");

Если НЕ НайденнаяСсылка.Пустая() Тогда
    // 1. Получаем ОБЪЕКТ из ссылки
    ДокОбъект = НайденнаяСсылка.ПолучитьОбъект();

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

    // 3. (Опционально) Вносим другие изменения в реквизиты или таблицы
    // ДокОбъект.Комментарий = "Изменено через COM";

    // 4. Корректно записываем с отменой проведения
    ДокОбъект.Записать(БазаУПП.РежимЗаписиДокумента.ОтменаПроведения);

    Сообщить("Проведение документа успешно отменено!");
Иначе
    Сообщить("Документ не найден!");
КонецЕсли;

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

← На главную