Почему не записываются данные в независимый регистр сведений в 1С и как это исправить?

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

Разработчики в среде «1С:Предприятие» часто сталкиваются с ситуацией, когда программный код записи в регистр сведений выполняется без видимых ошибок и исключений, однако при проверке в режиме предприятия регистр остается абсолютно пустым. Особенно часто это происходит при попытке сделать запись в независимый непериодический регистр сведений из модуля документа в момент его проведения или записи.

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

Выясним причину: почему код выполняется без ошибок, но данных в регистре нет?

Для начала проанализируем исходный код, который часто пытаются использовать начинающие разработчики:


НаборЗаписей = РегистрыСведений.Регистр.СоздатьНаборЗаписей();
Для Каждого Стр Из Строки Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Измренение1 = Стр.Реквизит1; 
    НоваяЗапись.Измренение2 = Стр.Реквизит2;
КонецЦикла;
НаборЗаписей.Записать();

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

1. Опечатка в именах свойств и «тихое» подавление ошибок

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

  1. Код обернут в конструкцию Попытка ... Исключение, в которой блок обработки ошибки оставлен пустым. Это крайне опасный паттерн программирования, который «проглатывает» любые критические сбои.
  2. Код выполняется в фоновом задании на сервере. В этом случае все ошибки не выводятся на экран пользователю, а записываются исключительно в Журнал регистрации.

2. Опасность полного замещения регистра (запись набора без отбора)

Метод НаборЗаписей.Записать() по умолчанию вызывается с неявным параметром замещения, равным Истина. Это означает, что платформа пытается заменить в базе данных все записи, соответствующие установленному отбору, на те, которые находятся в наборе в данный момент.

Если в объекте НаборЗаписей вообще не установлен отбор, платформа считает, что область действия набора — это весь регистр сведений целиком. В результате происходит следующее:

3. Откат транзакции при записи или проведении документа

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

Если после выполнения вашего кода в самом документе (или в других подписках на события) возникнет ошибка проведения, или если программно будет взведен флаг Отказ = Истина, транзакция будет полностью отменена. Все изменения в базе данных, включая успешную до этого запись в независимый регистр сведений, будут безвозвратно аннулированы.

4. Игнорирование уникальности ключа записи

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

---

Разберем правильные варианты реализации

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

Вариант А. Запись набора с обязательной установкой отбора

Этот вариант является наиболее производительным и правильным с точки зрения архитектуры 1С. Мы всегда должны ограничивать набор записей конкретным отбором по ключевому измерению (например, по конкретному контрагенту, номенклатуре или документу-источнику, если он выступает измерением регистра).


// Перед началом работы убедимся, что у нас есть данные для обработки
Если Строки.Количество() > 0 Тогда

    НаборЗаписей = РегистрыСведений.Регистр.СоздатьНаборЗаписей();
    
    // Обязательно устанавливаем отбор! Без него мы рискуем очистить весь регистр.
    // Например, отбор по измерению "Организация" или "ДокументСсылка"
    НаборЗаписей.Отбор.Измерение1.Установить(Стр.Реквизит1); 
    
    Для Каждого Стр Из Строки Цикл
        // Добавляем запись в набор и заполняем свойства без опечаток
        НоваяЗапись = НаборЗаписей.Добавить();
        НоваяЗапись.Измерение1 = Стр.Реквизит1; 
        НоваяЗапись.Измерение2 = Стр.Реквизит2;
    КонецЦикла;
    
    // Метод удалит из базы только те записи, которые соответствуют нашему Отбору, 
    // и запишет на их место новые строки из набора.
    Попытка
        НаборЗаписей.Записать(Истина); 
    Исключение
        // Обязательно фиксируем ошибку в Журнале регистрации для последующего анализа
        ЗаписьЖурналаРегистрации("ПрограммнаяЗаписьРегистра", 
            УровеньЖурналаРегистрации.Ошибка, , , 
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение; // Пробрасываем ошибку дальше, чтобы не маскировать сбой
    КонецПопытки;

КонецЕсли;

Вариант Б. Добавление новых записей без замещения старых

Если ваша задача — просто добавить новые уникальные записи к уже существующим в регистре, не удаляя при этом старые данные, то необходимо использовать параметр замещения Ложь в методе Записать().


НаборЗаписей = РегистрыСведений.Регистр.СоздатьНаборЗаписей();

Для Каждого Стр Из Строки Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Измерение1 = Стр.Реквизит1;
    НоваяЗание.Измерение2 = Стр.Реквизит2;
КонецЦикла;

Попытка
    // Параметр ЛОЖЬ указывает системе, что очищать старые записи регистра не нужно.
    // Важно: если в базе уже существует запись с аналогичным ключом (Измерение1 + Измерение2),
    // платформа вызовет ошибку нарушения уникальности.
    НаборЗаписей.Записать(Ложь); 
Исключение
    ЗаписьЖурналаРегистрации("ОшибкаДобавленияЗаписей", 
        УровеньЖурналаРегистрации.Предупреждение, , , 
        "Не удалось добавить новые записи: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Вариант В. Использование менеджера записи (построчно)

Этот способ хорошо подходит для точечной записи единичных строк или обновления существующих. Использовать его в циклах для записи сотен или тысяч строк не рекомендуется из-за снижения производительности (каждый вызов метода Записать() у менеджера записи выполняет отдельное обращение к СУБД).


Для Каждого Стр Из Строки Цикл
    
    МенеджерЗаписи = РегистрыСведений.Регистр.СоздатьМенеджерЗаписи();
    
    // Заполняем ключевые поля (измерения)
    МенеджерЗаписи.Измерение1 = Стр.Реквизит1;
    МенеджерЗаписи.Измерение2 = Стр.Реквизит2;
    
    // Если запись с такими измерениями уже есть в базе, метод Записать() 
    // обновит ее ресурсы. Если записи нет — создаст новую.
    Попытка
        МенеджерЗаписи.Записать();
    Исключение
        ЗаписьЖурналаРегистрации("ОшибкаМенеджераЗаписи", 
            УровеньЖурналаРегистрации.Ошибка, , , 
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
    
КонецЦикла;

Применяя эти паттерны программирования, контролируя контекст выполнения и не допуская опечаток в именах метаданных, вы гарантируете стабильную и предсказуемую работу с регистрами сведений в любых конфигурациях на базе платформы «1С:Предприятие 8».

← На главную