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