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