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