Как автоматически создать документ с задержкой после проведения другого документа в 1С?

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

В процессе автоматизации бизнес-процессов часто возникает задача: после того как пользователь провел один документ (назовем его "Документ 1"), система должна автоматически, но с некоторой задержкой (например, 3-5 минут), создать связанный с ним "Документ 2". Прямое создание в подписке на событие ОбработкаПроведения не подходит, так как задержка нужна для того, чтобы пользователь мог внести финальные правки в исходный документ. Рассмотрим, как реализовать такой механизм надежно и управляемо.

Проанализируем ситуацию. Основная сложность заключается в том, чтобы отделить момент фиксации события (проведение "Документа 1") от момента выполнения действия (создание "Документа 2"). Кроме того, решение должно быть устойчиво к таким факторам, как закрытие сеанса пользователем или повторное проведение исходного документа.

Надежный способ: Регистр сведений и Регламентное (фоновое) задание

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

Шаг 1. Создаем "очередь" задач с помощью регистра сведений

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

Структура регистра может быть следующей:

  1. Измерения:
    • ДокументОснование (Тип: ДокументСсылка) - Ссылка на "Документ 1", который инициировал создание.
  2. Ресурсы:
    • ДатаДобавления (Тип: Дата) - Время, когда запись была добавлена в очередь. Нужно для отсчета задержки.
    • СозданныйДокумент (Тип: ДокументСсылка) - Ссылка на уже созданный "Документ 2" для контроля дублей.
    • ТекстОшибки (Тип: Строка) - Для записи информации об ошибках при создании документа.
  3. Реквизиты:
    • СтатусОбработки (Тип: ПеречислениеСсылка.СтатусыОбработкиОчереди) - Статус текущей задачи. Значения перечисления: "КСозданию", "Обработан", "Ошибка".

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

Шаг 2. Фиксируем событие при проведении документа

Теперь нам нужно в момент проведения "Документа 1" добавить запись в нашу очередь. Для этого идеально подходит подписка на событие ОбработкаПроведения для нужного типа документа.

В модуле обработчика подписки разместим следующий код:


Процедура ДобавитьЗадачуНаСозданиеДокумента(Источник, Отказ, РежимПроведения)

    // Проверяем, что документ проводится в оперативном или неоперативном режиме
    Если РежимПроведения = РежимПроведенияДокумента.Оперативный 
        Или РежимПроведения = РежимПроведенияДокумента.Неоперативный Тогда
        
        // Создаем набор записей для нашего регистра
        НаборЗаписей = РегистрыСведений.ОчередьСозданияДокументов.СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.ДокументОснование.Установить(Источник.Ссылка);
        
        // Добавляем новую запись
        НоваяЗапись = НаборЗаписей.Добавить();
        НоваяЗапись.ДокументОснование = Источник.Ссылка;
        НоваяЗапись.ДатаДобавления = ТекущаяДата();
        НоваяЗапись.СтатусОбработки = Перечисления.СтатусыОбработкиОчереди.КСозданию;
        
        // Записываем набор. Если запись для этого документа-основания уже была, она перезапишется.
        НаборЗаписей.Записать();
        
    КонецЕсли;

КонецПроцедуры

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

Шаг 3. Обрабатываем очередь с помощью регламентного задания

Самая главная часть — это обработка нашей очереди. Для этого создадим регламентное задание, которое будет запускаться на сервере с определенной периодичностью (например, раз в 3 минуты).

  1. Создайте в конфигурации объект РегламентноеЗадание.
  2. В свойстве "Имя метода" укажите имя экспортной процедуры из общего серверного модуля, например, ОбработкаОчередиСозданияДокументов.ВыполнитьОбработку.
  3. Настройте для него расписание.

Теперь напишем код для самого обработчика в общем серверном модуле.


Процедура ВыполнитьОбработку() Экспорт

    // Определяем временную границу для задержки (например, 3 минуты)
    ДатаНачалаОбработки = ДобавитьКДате(ТекущаяДата(), "МИНУТА", -3);

    // Выбираем все задания, которые готовы к обработке
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ОчередьСозданияДокументов.ДокументОснование,
        |    ОчередьСозданияДокументов.СозданныйДокумент
        |ИЗ
        |    РегистрСведений.ОчередьСозданияДокументов КАК ОчередьСозданияДокументов
        |ГДЕ
        |    ОчередьСозданияДокументов.СтатусОбработки = &СтатусКСозданию
        |    И ОчередьСозданияДокументов.ДатаДобавления <= &ДатаНачалаОбработки";

    Запрос.УстановитьПараметр("СтатусКСозданию", Перечисления.СтатусыОбработкиОчереди.КСозданию);
    Запрос.УстановитьПараметр("ДатаНачалаОбработки", ДатаНачалаОбработки);

    Выборка = Запрос.Выполнить().Выбрать();

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

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

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

        Исключение
            // В случае ошибки записываем ее текст в регистр
            ТекстОшибки = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
            ОбновитьСтатусЗаписи(Выборка.ДокументОснование, Перечисления.СтатусыОбработкиОчереди.Ошибка, , ТекстОшибки);
        КонецПопытки;
        
    КонецЦикла;

КонецПроцедуры


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

Важные аспекты, которые необходимо учесть

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

1. Повторное проведение "Документа 1"

Пользователь может провести документ, затем отменить проведение, внести правки и провести его снова. Наш код в подписке на событие каждый раз будет обновлять запись в регистре, сбрасывая ДатаДобавления. Это и есть правильное поведение: отсчет задержки начнется заново после последнего изменения.

В коде регламентного задания мы предусмотрели проверку: если "Документ 2" уже был создан (поле СозданныйДокумент заполнено), мы не создаем новый, а обновляем существующий. Это критически важно для предотвращения дублирования документов.

2. Отмена проведения "Документа 1"

Если пользователь отменил проведение "Документа 1" до того, как регламентное задание сработало, наша проверка Если НЕ ДокументОснование.Проведен не даст создать "Документ 2". Это логично: нет основания — нет и связанного документа. Что делать, если "Документ 2" уже был создан, а "Документ 1" отменили — это вопрос бизнес-логики. Возможно, "Документ 2" тоже нужно пометить на удаление или отменить его проведение.

3. Обратная связь с пользователем

Пользователь должен понимать, что происходит в системе. Стоит добавить в форму "Документа 1" информацию о связанном с ним "Документе 2". Например, можно отображать гиперссылку на созданный документ, получая ее из нашего регистра ОчередьСозданияДокументов.

4. Обработка ошибок и мониторинг

Любой автоматический процесс может дать сбой (например, не заполнились обязательные реквизиты). Конструкция Попытка ... Исключение и запись текста ошибки в регистр позволяют администратору или ответственному лицу увидеть проблему, проанализировать ее и исправить вручную.

Альтернативный, но менее надежный метод: Обработка ожидания

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

Почему этот способ не рекомендуется для данной задачи?

Таким образом, для гарантированного выполнения отложенных серверных операций следует всегда выбирать связку "Регистр сведений + Регламентное задание". Этот подход обеспечивает надежность, управляемость, отказоустойчивость и является промышленным стандартом для решения подобных задач в 1С.

← На главную