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