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

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

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

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

Техническая реализация: алгоритм прямого изменения объекта

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

Разберем по шагам последовательность действий:

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

Рассмотрим пример кода, реализующий эту логику:


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

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

Проблема дублирования данных и «Ключ связи»

Проанализируем важный нюанс: что произойдет, если пользователь проведет «Документ 2» повторно? Без специальной проверки в табличную часть «Документа 1» каждый раз будет добавляться новая строка. Это приведет к неконтролируемому раздуванию документа и порче данных.

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

Блокировки и транзакционный контекст

Выясним причину, по которой опытные разработчики предостерегают от изменения документов внутри проведения. Когда выполняется процедура ОбработкаПроведения «Документа 2», система открывает транзакцию. Внутри этой же транзакции вы пытаетесь записать «Документ 1».

Рассмотрим основные риски:

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

Целостность данных при отмене проведения

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

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

Альтернатива: использование регистров — «золотой стандарт» 1С

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

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

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

Использование привилегированного режима и фоновых заданий

Если бизнес-логика все же требует изменения другого документа, разберем дополнительные технические приемы. Часто у пользователя, проводящего текущий документ, может не быть прав на редактирование «Документа 1» (например, период закрыт или не хватает ролей). В этом случае запись должна производиться в привилегированном модуле.

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

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

← На главную