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

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

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

Сценарий 1: Заполнение при создании нового документа

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

Рассмотрим, как правильно написать код в модуле объекта «Документ 1»:


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

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

Сценарий 2: Заполнение в уже существующих документах

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


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

Оптимальные методы получения данных

Разберем подробнее, как именно получить значение реквизита из другого документа, чтобы это работало быстро.

1. Использование функций БСП (Библиотеки стандартных подсистем)

Если ваша конфигурация построена на базе БСП (а это почти все современные решения: УТ 11, ERP, Бухгалтерия 3.0), самым правильным способом будет использование общего модуля ОбщегоНазначения. Функция ЗначениеРеквизитаОбъекта выполняет быстрый SQL-запрос только к тем полям, которые вам действительно нужны.


// Получение одного реквизита
НужноеЗначение = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаДок2, "НужныйРеквизит");

// Получение нескольких реквизитов (возвращает структуру)
ДанныеОснования = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаДок2, "Организация, Склад, Контрагент");
Если ДанныеОснования.Организация = ЭтотОбъект.Организация Тогда
    // Ваша логика
КонецЕсли;

2. Использование прямого запроса

Если БСП в конфигурации нет, напишем классический запрос. Это самый надежный и быстрый способ в среде 1С. Посмотрим на пример реализации функции в модуле объекта (с пометкой &НаСервере или в общем модуле):


Функция ПолучитьЗначениеИзОснования(СсылкаНаОснование)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Док2.НужныйРеквизит КАК Результат
        |ИЗ
        |   Документ.Док2 КАК Док2
        |ГДЕ
        |   Док2.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", СсылкаНаОснование);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Результат;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Как найти «Документ 2», если нет прямой ссылки?

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

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

Важные нюансы при записи и проведении

Рассмотрим ситуацию: вы изменили реквизит в уже проведенном «Документе 1». Если этот реквизит влияет на движения (например, «Склад» или «Ставка НДС»), то простая запись объекта методом ЭтотОбъект.Записать(РежимЗаписиДокумента.Запись) обновит данные в карточке документа, но не обновит данные в отчетах.

Для корректного отражения изменений необходимо:

Резюмируем: Для новых документов используем ОбработкаЗаполнения, для существующих — ПередЗаписью. Для получения данных всегда отдаем предпочтение запросам или функциям БСП, чтобы не перегружать сервер лишним чтением тяжелых объектов. Такой подход обеспечит быструю и стабильную работу вашей конфигурации 1С.

← На главную