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

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

Рассмотрим распространенную задачу для начинающих 1С-разработчиков: необходимо, чтобы при создании нового элемента в справочнике система автоматически находила определенные документы и добавляла в их табличные части новую строку, связанную с этим элементом. Например, при добавлении нового ученика в справочник, нужно добавить его во все будущие журналы посещаемости.

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

В чем заключается основная проблема: "невидимые" изменения

Чаще всего логику обновления документов размещают в модуле формы элемента справочника, в обработчике события ПослеЗаписиНаСервере. Этот обработчик выполняется, когда новый элемент уже сохранен в базе данных, что позволяет нам работать с его ссылкой.

Начинающий разработчик пишет код, который в цикле обходит найденные документы, добавляет в них строки, но по факту данные в документах не появляются. Давайте посмотрим на пример такого кода с ошибкой:


&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    
    // ... Код запроса для поиска нужных документов ...
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        // 1. Получаем объект документа из базы
        // 2. Добавляем в него строку
        Строка = Выборка.Ссылка.ПолучитьОбъект().Успеваемость.Добавить();
        Строка.Ученик = ТекущийОбъект.Ссылка;
        Строка.Оценка = 0;
        Строка.Присутствие = Ложь; 
        
        // 3. СНОВА получаем объект из базы и записываем его
        <strong>Выборка.Ссылка.ПолучитьОбъект().Записать(РежимЗаписиДокумента.Проведение);</strong>
    КонецЦикла;
        
КонецПроцедуры

Ключевая ошибка скрыта в последней строке цикла. Разберем, что происходит:

  1. Выборка.Ссылка.ПолучитьОбъект().Успеваемость.Добавить() — здесь мы получаем объект документа из базы данных, добавляем в его табличную часть новую строку в памяти сервера. На этом этапе объект в памяти уже изменен.
  2. Выборка.Ссылка.ПолучитьОбъект().Записать(...) — а вот здесь мы повторно обращаемся к базе данных по той же ссылке и получаем новый, "чистый" экземпляр объекта, в котором еще нет наших изменений. Именно этот неизмененный объект мы и пытаемся записать. В результате все изменения, сделанные на шаге 1, просто теряются.

Это одна из самых частых логических ошибок при работе с объектной моделью 1С. Важно помнить, что каждый вызов метода ПолучитьОбъект() — это новое чтение данных из базы.

Правильное решение: работаем с объектом через переменную

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

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


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

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

Дополнительные аспекты и возможные проблемы

Даже с правильным кодом можно столкнуться с трудностями. Проанализируем несколько важных моментов, которые стоит проверить, если что-то идет не так.

1. Проверка условия в запросе

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

2. События записываемого документа

Когда вы программно вызываете метод ОбъектДокумента.Записать(), для этого документа срабатывает вся стандартная цепочка событий:

Если в одном из этих обработчиков в модуле документа "ЖурналПосещаемости" есть какая-либо проверка (например, на заполненность других полей) и она не выполняется, то запись будет отменена (часто без выдачи ошибки в вашем коде). Поставьте точки останова в этих обработчиках в модуле изменяемого документа и проверьте, не происходит ли там отказ от записи (Отказ = Истина).

3. Производительность и блокировки

Представленный подход хорошо работает для небольшого количества документов. Однако, если запрос находит сотни или тысячи документов, которые нужно обновить, вы столкнетесь с серьезными проблемами:

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

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

← На главную