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