Как программно заполнить реквизиты в проведенных документах 1С и избежать ошибок при записи

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

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

Анализ типичных ошибок при переборе документов

Часто начинающие программисты пытаются решить задачу "в лоб", используя несколько последовательных запросов и вложенные циклы. Разберем, почему такой подход (представленный в первом сообщении темы) работает некорректно:

  1. Разрыв логики в циклах: Если вы сначала выбираете список сотрудников в один массив, а затем пытаетесь использовать эти данные в другом цикле, возникает путаница. Как было отмечено в обсуждении, переменная СотрудникПродление вне основного цикла будет всегда хранить значение только последней итерации, что приводит к заполнению только одного документа.
  2. Неэффективный поиск: Использование метода НайтиПоРеквизиту внутри цикла — это "антипаттерн". Каждый вызов этого метода заставляет платформу выполнять отдельный запрос к базе данных. Если документов тысячи, такая обработка будет работать крайне медленно. Более того, если у одного сотрудника несколько документов продления, метод вернет только один (самый первый найденный), проигнорировав остальные.
  3. Избыточность кода: Написание трех разных запросов там, где можно обойтись одним, усложняет отладку и поддержку кода.

Оптимальное решение: Один запрос с левым соединением

Самый эффективный способ — собрать все необходимые данные одним запросом. Мы выберем документы ПродлениеКонтрактаДоговора, у которых не заполнены нужные поля, и сразу "подтянем" к ним актуальные данные из регистра сведений ОсновныеДанныеКонтрактаДоговораСотрудника.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Док.Ссылка КАК Ссылка,
    |	ЕСТЬNULL(ДанныеРегистра.НомерДоговораКонтракта, """") КАК НомерИзРегистра,
    |	ЕСТЬNULL(ДанныеРегистра.ДатаДоговораКонтракта, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаИзРегистра
    |ИЗ
    |	Документ.ПродлениеКонтрактаДоговора КАК Док
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОсновныеДанныеКонтрактаДоговораСотрудника.СрезПоследних(&Период, ) КАК ДанныеРегистра
    |		ПО Док.Сотрудник = ДанныеРегистра.Сотрудник
    |ГДЕ
    |	Док.ТрудовойДоговорНомер = """"
    |	И Док.ТрудовойДоговорДата = ДАТАВРЕМЯ(1, 1, 1)
    |	И Док.Проведен = ИСТИНА";

Запрос.УстановитьПараметр("Период", ТекущаяДата()); // Или дата актуальности данных

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

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

В этом решении мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Это позволяет нам получить таблицу, где каждой строке документа соответствует строка с данными из регистра. Если данных в регистре нет, поля НомерИзРегистра и ДатаИзРегистра примут значения NULL, которые мы корректно обрабатываем функцией ЕСТЬNULL.

Как избежать ошибки «Запись с такими ключевыми полями уже существует»

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

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

Установив ДокОбъект.ОбменДанными.Загрузка = Истина, мы сообщаем системе, что документ записывается в особом режиме. При этом:

Пример безопасной записи:


ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
ДокОбъект.ТрудовойДоговорНомер = Выборка.НомерИзРегистра;
ДокОбъект.ТрудовойДоговорДата  = Выборка.ДатаИзРегистра;

// Включаем режим загрузки, чтобы обойти проверки и блокировки
ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Записать(); 

Важно: Помните, что при использовании ОбменДанными.Загрузка = Истина записи в связанных регистрах сведений не обновятся автоматически. Если вам нужно, чтобы измененные номер и дата договора попали в отчеты, использующие данные регистров, вам все же придется обеспечить корректное проведение документа или вручную обновить записи в соответствующих регистрах.

Работа с виртуальными таблицами и периодами

Проанализируем еще один важный момент: использование виртуальной таблицы СрезПоследних. В запросе выше мы передали &Период. Выясним причину, почему это критично:

Если мы не укажем период, 1С возьмет данные на самую последнюю дату в системе (обычно на "сейчас"). Однако, если вы правите документы за прошлые периоды, данные о договоре сотрудника могли измениться. Правильнее всего в соединении запроса использовать дату самого документа, чтобы получить те данные, которые были актуальны на момент его создания.

Рекомендации по производительности

Если количество исправляемых документов исчисляется тысячами, выполнение всей операции в одной транзакции может привести к длительным блокировкам базы. Рассмотрим, как оптимизировать процесс:

  1. Разделение на порции: Фиксируйте транзакцию каждые 100–200 документов.
  2. Индексация: Убедитесь, что поля, по которым идет соединение (например, Сотрудник), индексированы. В типовых конфигурациях основные кадровые реквизиты обычно уже имеют индексы.
  3. Проверка на изменения: Перед записью объекта проверьте, действительно ли новые данные отличаются от старых. Это сэкономит время на ненужной перезаписи данных на диске.

Выполняя эти рекомендации, мы создадим надежный и быстрый инструмент для исправления учетных данных, который не "положит" сервер и корректно обработает все связи объектов.

← На главную