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

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

В процессе разработки или администрирования баз данных 1С часто возникает задача массового заполнения или корректировки данных. Одной из типичных ситуаций является заполнение пустого реквизита в существующем документе (например, ПоступлениеТоваровУслуг) значением из его документа-основания (например, ЗаказПоставщику). При этом критически важно внести изменения так, чтобы документы не «слетели» с проведения, а просто обновили свои данные.

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

Базовый алгоритм решения задачи

Разберем логику, которую необходимо реализовать. Представим, что у нас есть Документ 1 (Приемник) и Документ 2 (Источник). Между ними установлена связь (обычно через стандартный реквизит ДокументОснование). Нам нужно:

  1. Выбрать все документы Приемники.
  2. Проверить, заполнен ли в них целевой реквизит (например, Проект).
  3. Если реквизит пуст, найти документ Источник.
  4. Получить значение реквизита из Источника и перенести его в Приемник.
  5. Записать Приемник без выполнения процедуры проведения.

Рассмотрим простейший пример кода, который решает эту задачу «в лоб», используя выборку:


ДокументыПриемники = Документы.ПоступлениеТоваровУслуг.Выбрать();

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

Важный момент: использование метода Записать(РежимЗаписиДокумента.Запись) гарантирует, что документ сохранит свой статус (проведен или не проведен), не создаст новых движений в регистрах и не изменит существующие. Это именно то, что требовалось в условии задачи.

Оптимизация: использование запросов

Метод Выбрать() удобен для небольших баз, но если документов десятки тысяч, перебор всех объектов станет крайне медленным. Проанализируем, как сделать это эффективнее. Правильный подход — использовать Запрос, чтобы сразу получить только те документы, которые нуждаются в исправлении.

Рассмотрим пример запроса, который выберет только нужные нам ссылки:


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

РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    ОбъектДок = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
    ОбъектДок.Проект = ВыборкаДетальныеЗаписи.ПроектИзОснования;
    ОбъектДок.Записать(РежимЗаписиДокумента.Запись);
КонецЦикла;

В этом случае система не будет «просматривать» лишние документы, что значительно ускорит работу обработки.

Использование свойства «ОбменДанными.Загрузка»

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

Проанализируем обновленный участок кода:


ОбъектДок = Ссылка.ПолучитьОбъект();
ОбъектДок.Проект = НужноеЗначение;
// Устанавливаем флаг, чтобы пропустить типовые проверки
ОбъектДок.ОбменДанными.Загрузка = Истина; 
ОбъектДок.Записать(РежимЗаписиДокумента.Запись);

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

Работа с составными типами данных

Часто реквизит ДокументОснование имеет составной тип. Если мы попытаемся обратиться к реквизиту Проект у основания, которое не является Заказом (например, основание — Возврат), система может выдать ошибку или вернуть Неопределено. Разберем, как правильно проверять тип документа перед обработкой:


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

Безопасность и быстродействие: дополнительные советы

Выясним, какие еще приемы помогут сделать обработку надежной:

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

Подводя итог, можно сказать, что задача заполнения реквизитов решается довольно просто, если соблюдать иерархию действий: точный отбор через запрос -> получение объекта -> модификация данных -> запись в режиме "Запись". Этот подход сохраняет целостность данных в регистрах и экономит ресурсы сервера.

← На главную