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

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

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

Проблема прямой перезаписи и оптимистическая блокировка

Рассмотрим ситуацию, с которой часто сталкиваются начинающие разработчики. При попытке записать Документ1 из модуля объекта Документ2 система может выдать ошибку: «Объект изменен или заблокирован». Проанализируем причину: это результат работы механизма оптимистической блокировки. Если у пользователя в этот момент открыта форма Документ1, и код в Документ2 меняет этот документ в базе данных, то версия объекта в памяти (в форме) становится неактуальной. При попытке пользователя сохранить открытый Документ1, система увидит несовпадение версий и заблокирует запись.

Решение 1. Использование обработчиков событий в модуле объекта

Если архитектура решения требует прямой модификации реквизитов, наиболее логичным местом для размещения кода являются процедуры ОбработкаПроведения и ОбработкаУдаленияПроведения в модуле объекта Документ2. Это гарантирует, что изменения будут внесены независимо от того, проводится документ из формы или из формы списка.

Рассмотрим пример кода, который безопасно пытается изменить документ-основание:


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

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

Решение 2. Архитектурно верный подход через Регистры Сведений

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

Разберем преимущества этого метода:

  1. Отсутствие блокировок: Запись в регистр не блокирует сам документ. Пользователь может спокойно редактировать Документ1, пока Документ2 обновляет данные в регистре.
  2. Целостность данных: При отмене проведения Документ2 записи в регистре удаляются автоматически платформой, и Документ1 снова «видит» исходное состояние.
  3. Производительность: Запись в одну таблицу регистра происходит быстрее, чем полная перезапись тяжелого объекта документа со всеми его табличными частями.

Чтобы отобразить эти данные в форме Документ1, достаточно в процедуре ПриЧтенииНаСервере или через механизм динамических списков подтянуть значения из регистра.

Решение 3. Синхронизация через механизм оповещений

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

В форме Документ2 после успешного проведения или отмены проведения добавим код:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Оповещаем систему об изменении данных
    Оповестить("ИзмененДокументОснование", ПараметрыЗаписи, Объект.ДокументОснование);
КонецПроцедуры

А в форме Документ1 настроим обработчик:


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    Если ИмяСобытия = "ИзмененДокументОснование" И Источник = Объект.Ссылка Тогда
        // Перечитываем данные с сервера, чтобы обновить форму без потери данных
        ЭтаФорма.Прочитать();
    КонецЕсли;
КонецПроцедуры

Решение 4. Использование подписок на события

Проанализируем ситуацию, когда мы не хотим вносить изменения в типовые модули объектов, чтобы не усложнять будущие обновления конфигурации. В этом случае на помощь приходят Подписки на события. Мы можем создать общую подписку на события ОбработкаПроведения и ОбработкаУдаленияПроведения для объекта Документ2.

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

Важные рекомендации и итоги

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

Основные правила безопасности при работе со связанными объектами:

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

← На главную