Как изменить данные при заполнении на основании, не затрагивая исходный документ?

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

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

Разберем механизм работы параметра ДанныеЗаполнения

Прежде чем приступать к кодингу, проанализируем, что именно попадает в параметр ДанныеЗаполнения обработчика события ОбработкаЗаполнения. Поведение платформы зависит от способа инициации создания нового объекта:

  1. Интерактивное создание (кнопка «Создать на основании»): В этом случае платформа передает в параметр ДанныеЗаполнения Ссылку (например, ДокументСсылка.РеализацияТоваровУслуг). Ссылка является неизменяемым типом данных. Чтобы прочитать из нее данные табличной части, нам не нужно ничего менять в источнике. Однако, если мы попытаемся программно изменить реквизиты через ДанныеЗаполнения.ПолучитьОбъект(), важно помнить: пока мы не вызвали метод Записать(), изменения в базе данных не появятся.
  2. Программное создание (метод Заполнить()): Здесь в параметр ДанныеЗаполнения попадет именно то, что передал разработчик в качестве аргумента. Это может быть ссылка, структура, таблица значений или даже уже созданный в памяти объект (ДокументОбъект).

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

Способ 1. Использование расширения и подмена типа данных на Структуру

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

Посмотрим на пример кода в расширении с использованием аннотации &Вместо:


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

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

Важный нюанс: Как отмечалось в обсуждении, многие типовые конфигурации (ERP, УТ 11) проверяют тип параметра ДанныеЗаполнения. Если программа ожидает строго ссылку, а получает структуру, заполнение может не произойти. В таких случаях этот метод потребует дополнительной адаптации вызываемых функций.

Способ 2. Получение объекта в памяти (без записи)

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

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


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

Способ 3. Использование метода Скопировать()

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

Рассмотрим алгоритм действий:

  1. Проверяем тип передаваемых данных.
  2. Если это объект, создаем его копию методом НовыйОбъект = ДанныеЗаполнения.Скопировать().
  3. Если это ссылка, получаем объект и также можем его копировать для чистоты эксперимента.
  4. Работаем с НовыйОбъект. Любые манипуляции с ним никак не затронут оригинал.

Проблема типовых проверок и безопасный «Best Practice»

Разберем ситуацию, когда в конфигурации используется сложная логика в общих модулях. Часто там можно встретить код вида: Если ТипЗнч(ДанныеЗаполнения) <> Тип("ДокументСсылка.ИмяДокумента") Тогда Возврат; КонецЕсли;.

Чтобы обойти такие ограничения и не переписывать половину системы, проанализируем следующие рекомендации:

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

Во-вторых, если нужно изменить логику именно «до», используйте расширение с типом вызова &Вместо, но внутри него не меняйте сам параметр ДанныеЗаполнения, а подготовьте свою таблицу значений на основе источника и заполните ЭтотОбъект вручную, установив СтандартнаяОбработка = Ложь.

Пример реализации с отменой стандартной обработки:


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

Подведем итоги

Мы рассмотрели различные сценарии работы с процедурой ОбработкаЗаполнения. Главный вывод: работа с данными в 1С достаточно безопасна, если понимать разницу между ссылкой и объектом. Изменение ОбъектИсточник.ТабличнаяЧасть в памяти без вызова Записать() никогда не затронет данные в базе. Однако для предотвращения изменения данных в открытых формах пользователя всегда лучше оперировать копиями данных или структурами.

← На главную