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