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