Как программно создать и записать документ через Web-сервис в 1С без ошибок?

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

Разработка интеграционных решений на платформе 1С — задача, с которой рано или поздно сталкивается каждый программист. Использование Web-сервисов (SOAP или REST HTTP) является стандартным и очень удобным способом обмена данными между 1С и внешними системами. Однако при попытке программного создания, заполнения и проведения документов (например, таких как РасходныйОрдерНаТовары или РеализацияТоваровУслуг) непосредственно внутри кода Web-сервиса разработчики часто сталкиваются с целым букетом ошибок. Программа отказывается записывать документы, ругается на отсутствующие процедуры, выдает ошибки контекста выполнения или аварийно завершает работу при вызове подписок на события.

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

Разбираем проблему препроцессора: почему директива #Если НЕ ВнешнееСоединение не работает?

При переносе или адаптации кода, который изначально создавался для интерактивной работы пользователя в интерфейсе, разработчики часто пытаются «отсечь» клиентские вызовы (такие как предупреждения, диалоги, открытие форм) с помощью инструкций препроцессора. Распространенная ошибка заключается в неверном понимании контекстов выполнения. Проанализируем, почему директива #Если НЕ ВнешнееСоединение не спасает от ошибок компиляции в Web-сервисе, в то время как #Если Клиент решает проблему.

Давайте разберем контексты выполнения на системном уровне:

  1. Веб-сервис (Web-сервис) всегда работает исключительно на Сервере. Сеанс Web-сервиса — это фоновый серверный процесс.
  2. Внешнее соединение (COM/OLE) — это отдельный специфический вид подключения (например, когда одна база данных подключается к другой через COM-соединение V83.COMConnector).

Когда код модуля документа выполняется в рамках вызова Web-сервиса, платформа проверяет условие #Если НЕ ВнешнееСоединение. Поскольку текущий сеанс является серверным сеансом Web-сервиса (а не COM-соединением), это условие возвращает Истина. В результате препроцессор пытается скомпилировать весь код, находящийся внутри этой директивы. Если внутри этого блока содержатся вызовы клиентских процедур общего модуля или интерактивные методы (например, Предупреждение() или Вопрос()), серверная компиляция завершится ошибкой, так как эти методы физически отсутствуют на сервере.

Директива #Если Клиент отрабатывает корректно, потому что для сервера Web-сервиса контекст Клиент всегда равен Ложь. Весь код внутри этого блока просто отбрасывается при компиляции на сервере.

Для правильного разделения контекста в модулях, которые могут вызываться как интерактивно, так и через Web-сервисы, фоновые задания или COM-соединения, мы рекомендуем использовать следующую конструкцию:


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

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

Решаем ошибку «Метод обработчика события не найден» в подписках на события

Вторая частая проблема при записи документов через Web-сервис — падение системы с критической ошибкой: «При подписке на событие произошла ошибка. Метод обработчика события не найден». Выясним причину этого поведения.

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

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

  1. Откройте конфигуратор и найдите подписку на событие, которая вызывает ошибку (ее имя обычно указано в детальном описании ошибки в Журнале регистрации).
  2. В свойствах подписки посмотрите, на какой Общий модуль и какую конкретно процедуру она ссылается.
  3. Найдите этот общий модуль в дереве метаданных, откройте его свойства (клавиша F4) и проверьте установленные флаги:
    • Флаг Сервер (Server) должен быть обязательно включен. Если он выключен, сервер 1С не сможет скомпилировать этот модуль при вызове из Web-сервиса.
    • Флаг Внешнее соединение (External Connection) также крайне рекомендуется включить, чтобы избежать аналогичных ошибок при обмене по COM.
    • Флаг Глобальный (Global) у модулей-обработчиков подписок должен быть выключен (согласно стандартам разработки фирмы «1С»).
  4. Перейдите в текст самого общего модуля и убедитесь, что процедура-обработчик объявлена с ключевым словом Экспорт.

После включения флага «Сервер» для общего модуля платформа получит доступ к процедуре-обработчику подписки в контексте Web-сервиса, и ошибка исчезнет.

Программные способы обхода интерактивных проверок при записи документа

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

Способ А. Режим загрузки данных (ОбменДанными.Загрузка = Истина)

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


НовыйДокумент = Документы.РасходныйОрдерНаТовары.СоздатьДокумент();
// Заполнение реквизитов документа данными из Web-сервиса...
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.НайтиПоКоду("000000001");

// Включаем режим загрузки данных
НовыйДокумент.ОбменДанными.Загрузка = Истина;

// Записываем документ с проведением
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);

Как это работает: на уровне платформы 1С при установке флага Загрузка = Истина отключается большинство системных проверок (например, проверка уникальности номеров, контроль остатков на уровне типовых механизмов). Более того, во всех типовых конфигурациях (включая «Управление торговлей», «ERP», «Комплексная автоматизация») в самом начале обработчиков ПередЗаписью, ПриЗаписи и ОбработкаПроведения всегда прописано стандартное условие:


Если ОбменДанными.Загрузка Тогда
    Возврат;
КонецЕсли;

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

Способ Б. Использование свойства «ДополнительныеСвойства»

Если режим полной загрузки вам не подходит (например, вам все же необходимо, чтобы выполнились стандартные расчеты или движения по регистрам, но нужно пропустить только конкретные вызовы клиентского кода), используйте структуру ДополнительныеСвойства, которая есть у любого прикладного объекта 1С.

В коде Web-сервиса при создании документа передайте в эту структуру специальный флаг:


НовыйДокумент = Документы.РасходныйОрдерНаТовары.СоздатьДокумент();
// Заполнение реквизитов...
НовыйДокумент.ДополнительныеСвойства.Вставить("СозданЧерезВебСервис", Истина);
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, запущен ли процесс из Web-сервиса
    Если ДополнительныеСвойства.Свойство("СозданЧерезВебСервис") Тогда
        // Пропускаем интерактивные проверки или выполняем облегченный алгоритм
        Возврат;
    КонецЕсли;
    
    // Стандартные тяжелые проверки для обычных пользователей...
    
КонецПроцедуры

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

Архитектурный паттерн: Асинхронное создание документов через регистр-очередь

Несмотря на наличие программных способов обхода проверок, создание и проведение сложных документов непосредственно в транзакции Web-сервиса в режиме реального времени (синхронно) считается плохой практикой (Anti-pattern). Посмотрим на причины этого:

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

Шаг 1. Создание регистра сведений для очереди

Создайте новый регистр сведений, например, ОчередьЗадачИнтеграции (независимый, непериодический).

Определите структуру регистра:

Шаг 2. Быстрый прием данных в Web-сервисе

Код Web-сервиса должен выполнять только одну задачу — быстро принять JSON/XML, записать его в регистр со статусом «Новый» и мгновенно вернуть клиенту успешный ответ с идентификатором задачи.


Функция СоздатьДокументАсинхронно(ДанныеЗапроса) Экспорт
    
    ИдентификаторЗадачи = Новый УникальныйИдентификатор();
    
    // Записываем входящий пакет данных в очередь
    МенеджерЗаписи = РегистрыСведений.ОчередьЗадачИнтеграции.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.ИдентификаторЗадачи = ИдентификаторЗадачи;
    МенеджерЗаписи.Статус = Перечисления.СтатусыЗадачИнтеграции.Новый;
    МенеджерЗаписи.ДатаСоздания = ТекущаяДата();
    МенеджерЗаписи.ДанныеПакета = Новый ХранилищеЗначения(ДанныеЗапроса);
    
    Попытка
        МенеджерЗаписи.Записать(Истина);
        Возврат "{\"status\": \"accepted\", \"job_id\": \"" + Строка(ИдентификаторЗадачи) + "\"}";
    Исключение
        Возврат "{\"status\": \"error\", \"message\": \"" + ОписаниеОшибки() + "\"}";
    КонецПопытки;
    
КонецФункции

Шаг 3. Обработка очереди регламентным заданием

Создайте регламентное задание, которое будет запускаться в фоновом режиме каждые 10–30 секунд и вызывать процедуру общего модуля для обработки новых задач.

Проанализируем пример кода обработчика очереди:


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

Функция РазобратьИСоздатьДокумент(ДанныеСтрокой)
    // Здесь должна быть ваша прикладная логика парсинга и создания документа
    // Например:
    // Док = Документы.РасходныйОрдерНаТовары.СоздатьДокумент();
    // ... заполнение полей ...
    // Док.Записать(РежимЗаписиДокумента.Проведение);
    // Возврат Док.Ссылка;
КонецФункции

Преимущества асинхронного подхода:

  1. Web-сервис работает мгновенно. Время ответа клиенту составляет миллисекунды, что исключает любые таймауты со стороны веб-сервера.
  2. Если при проведении документа возникнет ошибка (например, не хватило остатков на складе или заблокирован период), интеграция не сломается. Ошибка запишется в регистр сведений. Администратор базы сможет увидеть её в интерфейсе 1С, исправить причину (например, оприходовать товар) и нажать кнопку «Повторить обработку» прямо в мониторе задач, не требуя от внешней системы повторной отправки.
  3. Регламентное задание выполняется в фоновом режиме сервера, планомерно обрабатывая документы один за другим, снижая пиковые нагрузки на базу данных.

Используя приведенные рекомендации, вы сможете раз и навсегда решить проблемы с контекстом компиляции при вызове процедур из внешних источников и построить надежную, отказоустойчивую систему интеграции 1С с любыми веб-сервисами.

← На главную