В практике разработчика 1С часто возникает задача массового исправления данных в уже существующих документах. Например, когда в документах Продление контракта, договора не заполнены номер или дата трудового договора, хотя эти данные уже есть в системе (в регистре сведений или других документах). Рассмотрим, как правильно реализовать обработку для автоматического заполнения таких реквизитов, проанализируем типичные ошибки новичков и разберем наиболее эффективные методы работы с базой данных.
Часто начинающие программисты пытаются решить задачу "в лоб", используя несколько последовательных запросов и вложенные циклы. Разберем, почему такой подход (представленный в первом сообщении темы) работает некорректно:
СотрудникПродление вне основного цикла будет всегда хранить значение только последней итерации, что приводит к заполнению только одного документа.НайтиПоРеквизиту внутри цикла — это "антипаттерн". Каждый вызов этого метода заставляет платформу выполнять отдельный запрос к базе данных. Если документов тысячи, такая обработка будет работать крайне медленно. Более того, если у одного сотрудника несколько документов продления, метод вернет только один (самый первый найденный), проигнорировав остальные.Самый эффективный способ — собрать все необходимые данные одним запросом. Мы выберем документы ПродлениеКонтрактаДоговора, у которых не заполнены нужные поля, и сразу "подтянем" к ним актуальные данные из регистра сведений ОсновныеДанныеКонтрактаДоговораСотрудника.
Рассмотрим структуру такого запроса и последующий код обработки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| ЕСТЬNULL(ДанныеРегистра.НомерДоговораКонтракта, """") КАК НомерИзРегистра,
| ЕСТЬNULL(ДанныеРегистра.ДатаДоговораКонтракта, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаИзРегистра
|ИЗ
| Документ.ПродлениеКонтрактаДоговора КАК Док
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОсновныеДанныеКонтрактаДоговораСотрудника.СрезПоследних(&Период, ) КАК ДанныеРегистра
| ПО Док.Сотрудник = ДанныеРегистра.Сотрудник
|ГДЕ
| Док.ТрудовойДоговорНомер = """"
| И Док.ТрудовойДоговорДата = ДАТАВРЕМЯ(1, 1, 1)
| И Док.Проведен = ИСТИНА";
Запрос.УстановитьПараметр("Период", ТекущаяДата()); // Или дата актуальности данных
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем объект документа для редактирования
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Заполняем реквизиты
ДокОбъект.ТрудовойДоговорНомер = Выборка.НомерИзРегистра;
ДокОбъект.ТрудовойДоговорДата = Выборка.ДатаИзРегистра;
// Попытка записи
Попытка
// Используем режим проведения, если нужно обновить движения в регистрах
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Ошибка при записи документа " + Выборка.Ссылка + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
В этом решении мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Это позволяет нам получить таблицу, где каждой строке документа соответствует строка с данными из регистра. Если данных в регистре нет, поля НомерИзРегистра и ДатаИзРегистра примут значения NULL, которые мы корректно обрабатываем функцией ЕСТЬNULL.
При программном проведении кадровых документов (особенно в конфигурациях типа ЗУП 3.1) часто возникает ошибка дублирования записей в регистрах сведений. Проблема заключается в том, что документ при проведении пытается записать данные в регистр, в котором уже есть записи с таким же ключом (например, тот же Сотрудник и тот же Период).
Если ваша задача — только исправить реквизиты документа, не меняя логику движений в учете, проанализируем использование свойства ОбменДанными.Загрузка. Это "спасательный круг" для подобных задач.
Установив ДокОбъект.ОбменДанными.Загрузка = Истина, мы сообщаем системе, что документ записывается в особом режиме. При этом:
ПередЗаписью и ПриЗаписи.Пример безопасной записи:
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
ДокОбъект.ТрудовойДоговорНомер = Выборка.НомерИзРегистра;
ДокОбъект.ТрудовойДоговорДата = Выборка.ДатаИзРегистра;
// Включаем режим загрузки, чтобы обойти проверки и блокировки
ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Записать();
Важно: Помните, что при использовании ОбменДанными.Загрузка = Истина записи в связанных регистрах сведений не обновятся автоматически. Если вам нужно, чтобы измененные номер и дата договора попали в отчеты, использующие данные регистров, вам все же придется обеспечить корректное проведение документа или вручную обновить записи в соответствующих регистрах.
Проанализируем еще один важный момент: использование виртуальной таблицы СрезПоследних. В запросе выше мы передали &Период. Выясним причину, почему это критично:
Если мы не укажем период, 1С возьмет данные на самую последнюю дату в системе (обычно на "сейчас"). Однако, если вы правите документы за прошлые периоды, данные о договоре сотрудника могли измениться. Правильнее всего в соединении запроса использовать дату самого документа, чтобы получить те данные, которые были актуальны на момент его создания.
Если количество исправляемых документов исчисляется тысячами, выполнение всей операции в одной транзакции может привести к длительным блокировкам базы. Рассмотрим, как оптимизировать процесс:
Сотрудник), индексированы. В типовых конфигурациях основные кадровые реквизиты обычно уже имеют индексы.Выполняя эти рекомендации, мы создадим надежный и быстрый инструмент для исправления учетных данных, который не "положит" сервер и корректно обработает все связи объектов.