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

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

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

Разбор проблемы и типичные ошибки

Рассмотрим ситуацию, когда при записи документа ПредварительнаяКалькуляция необходимо зафиксировать его статус в регистре сведений СтатусыПК. Часто разработчики пытаются реализовать это в модуле формы или через событие ПередЗаписью, используя запросы к базе данных. Проанализируем пример кода, который часто приводит к ошибкам:


&НаСервере
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ Ссылка, СтатусыПК ИЗ Документ.ПредварительнаяКалькуляция";
    Результат = Запрос.Выполнить;
    Выборка = Результат.Выбрать();
    
    НаборЗаписей = РегистрыСведений.СтатусыПК.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Ссылка); // Ошибка: ссылка может быть еще не создана
    НоваяЗапись = НаборЗаписей.Добавить();
    // ... заполнение полей
    НаборЗаписей.Записать(Ложь); // Ошибка: попытка дописать в существующий набор
КонецПроцедуры

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

Выбор правильного события для записи

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

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

Использование коллекции Движения

Самый эффективный и правильный способ записи данных в подчиненный регистр — использование коллекции Движения. Если в конфигураторе на вкладке "Движения" документа указан соответствующий регистр сведений, нам не нужно вручную создавать набор записей и устанавливать отборы.

Рассмотрим пример реализации в процедуре ОбработкаПроведения:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // Очищаем старые записи, если это необходимо (зависит от настроек документа)
    Движения.СтатусыПК.Записывать = Истина;
    
    НоваяЗапись = Движения.СтатусыПК.Добавить();
    НоваяЗапись.Период = Дата;
    НоваяЗапись.ДокументПК = Ссылка;
    НоваяЗапись.Статус = СтатусыПК;
КонецПроцедуры

Важный момент: Установка флага Движения.СтатусыПК.Записывать = Истина сообщает платформе, что по завершении транзакции проведения набор записей этого регистра должен быть физически записан в базу данных. Это избавляет от необходимости вызывать метод Записать() вручную.

Программная запись через НаборЗаписей

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


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

Проанализируем ключевые отличия этого подхода:

Блокировки и транзакции

При записи в регистр внутри событий ПриЗаписи или ОбработкаПроведения важно помнить, что все действия выполняются в рамках одной транзакции. Если метод НаборЗаписей.Записать() вызовет исключение (например, из-за конфликта блокировок), то сам документ также не будет записан. Это критически важно для обеспечения целостности данных: у вас не может появиться ситуация, когда документ в базе есть, а его статуса в регистре нет.

Также стоит обратить внимание на свойство документа "Удаление движений" в Конфигураторе. Если установлено "Удалять автоматически", система сама очистит регистры при отмене проведения. Если же вы пишете в регистр вручную через НаборЗаписей в процедуре ПриЗаписи, автоматика платформы может не сработать так, как вы ожидаете, и вам придется вручную контролировать очистку данных при пометке документа на удаление.

Рекомендации по оптимизации

Рассмотрим еще несколько советов, которые помогут сделать ваш код надежнее:

  1. Проверка на ОбменДанными.Загрузка: Всегда добавляйте проверку Если ОбменДанными.Загрузка Тогда Возврат; КонецЕсли; в начале процедур записи, чтобы программная логика не мешала при переносе данных между базами.
  2. Минимизация данных: Записывайте в регистр только те измерения и ресурсы, которые действительно необходимы для отчетов. Не дублируйте всю информацию из документа.
  3. Индексация: Если вы планируете часто делать выборки из этого регистра по полям, отличным от регистратора, не забудьте установить флаг "Индексировать" для соответствующих измерений в конфигураторе.

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

← На главную