Как правильно изменить реквизит документа-основания при проведении подчиненного документа

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

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

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

Разберем типичную ситуацию: разработчик прописывает код изменения документа-основания непосредственно в процедуре ОбработкаПроведения текущего документа. Почему это может быть плохим решением? Во-первых, проведение документа всегда происходит внутри транзакции. Если при записи документа-основания произойдет ошибка (например, сработает проверка заполнения или документ окажется заблокирован другим пользователем), транзакция проведения текущего документа также будет отменена. Во-вторых, если документ-основание находится в закрытом для редактирования периоде, обычная запись будет заблокирована платформой.

Проанализируем основные способы решения этой задачи, начиная от самых «чистых» с точки зрения архитектуры и заканчивая практическими приемами для работы с существующими реквизитами.

Решение 1: Использование Регистра Сведений (Архитектурно верный подход)

Наиболее правильным решением является вынос изменяемого реквизита из состава документа в отдельный Регистр Сведений. Вместо того чтобы менять значение внутри объекта документа, мы записываем состояние в регистр, где измерением является ссылка на документ-основание, а ресурсом — необходимое значение.

Преимущества этого метода:

  1. Независимость от периода: Запись в регистр сведений не требует изменения самого документа, а значит, не нужно открывать период для бухгалтерии.
  2. Отсутствие блокировок: Мы не блокируем объект документа для редактирования другими пользователями.
  3. Скорость: Запись в регистр происходит значительно быстрее, чем перезапись всего объекта документа со всеми его табличными частями и проверками.

Для того чтобы пользователь мог видеть это значение в списке документов и выполнять по нему отборы, в 1С (особенно в обычных формах) можно использовать механизм Характеристик или соединение с таблицей регистра в динамическом списке (для управляемых форм). Рассмотрим пример кода для записи в регистр сведений при проведении:


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

Решение 2: Обновление реквизита документа через режим загрузки

Если архитектура конфигурации не позволяет использовать регистры (например, реквизит уже задействован в десятках отчетов и типовых механизмах), нам придется обновлять именно реквизит объекта. Чтобы избежать проблем с датами запрета и лишними проверками, используем свойство ОбменДанными.Загрузка = Истина.

Разберем по шагам, как это работает. Установка этого флага сообщает платформе, что запись объекта производится в режиме загрузки данных. В этом режиме пропускаются прикладные проверки в модулях (ПередЗаписью, ПриЗаписи), а также игнорируется проверка периода (в большинстве типовых конфигураций).

Посмотрим на пример реализации в подписке на событие или в модуле документа:


ОбъектОснование = ЭтотОбъект.ДокументОснование.ПолучитьОбъект();

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

Решение 3: Использование механизма ДополнительныеСвойства

Выясним причину, почему иногда использование записи «в лоб» приводит к конфликтам блокировок. Когда мы проводим документ «А» и внутри него записываем документ «Б», платформа накладывает блокировки на оба объекта. Если в этот момент другой процесс пытается сделать обратное, возникает Deadlock (взаимная блокировка).

Чтобы минимизировать время нахождения в транзакции и передать данные между обработчиками, используем структуру ДополнительныеСвойства. Мы можем пометить документ-основание как «требующий обновления» в модуле объекта, а саму запись произвести в подписке на событие ПриЗаписи или ОбработкаПроведения.

Рассмотрим, как это реализовать:

  1. В модуле документа в процедуре ОбработкаПроведения добавляем флаг: ЭтотОбъект.ДополнительныеСвойства.Вставить("ОбновитьОснование", Истина);.
  2. Создаем подписку на событие ПриЗаписи для этого типа документа.
  3. В обработчике подписки проверяем наличие флага и выполняем обновление основания.

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

Решение 4: Отложенное обновление (Очередь задач)

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

Вместо непосредственной записи документа-основания, мы фиксируем необходимость изменения в специальном регистре (назовем его «ОчередьОбновленияДокументов»).

Алгоритм работы:

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

Рекомендации для Обычных Форм (1С 8.3 / 8.2)

Поскольку в обсуждении упоминались обычные формы, стоит обратить внимание на удобство работы пользователя. Если вы выбрали вариант с Регистром Сведений, для отображения данных в списке документов (журнале) используйте событие ПриВыводеСтроки. Однако помните, что это событие сильно замедляет работу при больших списках.

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

Резюме

Подводя итог, можно выстроить следующую иерархию правильности решений:

  1. Регистр Сведений — лучший выбор для сохранения целостности и производительности.
  2. Запись с ОбменДанными.Загрузка = Истина — допустимый вариант для существующих реквизитов, позволяющий обойти запреты редактирования периодов.
  3. Очередь задач через фоновое задание — оптимально для высоконагруженных систем, где важна скорость отклика интерфейса.

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

← На главную