Запись в регистр сведений без регистратора: менеджер, набор, форма
Содержание
Одну запись в независимый регистр сведений
1С добавляют через менеджер записи: РегистрыСведений.Имя.СоздатьМенеджерЗаписи(), затем заполняют период, измерения и ресурсы и вызывают Записать(). Много записей сразу — через набор записей СоздатьНаборЗаписей() с отбором. Без кода — из формы списка регистра кнопкой «Создать».
Частое заблуждение: «Записать() всегда добавляет новую строку». Нет. По умолчанию запись с тем же ключом молча заменяется. А набор без отбора с параметром замещения стирает весь регистр. Всё, что ниже, — про то, как добавить запись и не потерять чужие данные. Механика общая для платформы 8.3 и не зависит от конфигурации.
Как независимый регистр сведений хранит записи
У регистра сведений два режима записи: «Подчинение регистратору» и «Независимый». В первом случае записи создаёт документ при проведении. Во втором — кто угодно: пользователь в форме, обработка, регламентное задание, обмен.
Каждая запись независимого регистра однозначно определяется ключом:
- 📅 Период — только если регистр периодический;
- 🔑 все измерения — в порядке, заданном в конфигураторе.
Ресурсы и реквизиты в ключ не входят. Две записи с одинаковым периодом и одинаковыми измерениями существовать не могут — это и есть уникальность на уровне платформы.
Периодичность и что она делает с датой
Периодичность задаётся в свойствах регистра: «в пределах секунды», «дня», «месяца», «квартала», «года» или «по позиции регистратора» (последняя — только для подчинённых регистров). При записи платформа приводит период к началу интервала.
Пример: регистр с периодичностью «в пределах месяца». Вы пишете запись с периодом 17.03, а в базу ляжет 01.03. Если на 01.03 с теми же измерениями запись уже есть, она будет заменена.
Перед загрузкой проверьте периодичность регистра. Две цены на 05.03 и 20.03 в регистре «в пределах месяца» превратятся в одну — последнюю записанную.
Свойства измерений, которые влияют на запись
| Свойство измерения | Что делает при добавлении записи |
|---|---|
| Запрет незаполненных значений | Пустое значение измерения — ошибка записи |
| Основной отбор | Измерение доступно в отборе набора и используется в механизмах обмена |
| Ведущее | Запись удаляется при удалении объекта, на который она ссылается |
| Тип | Значение другого типа не запишется или будет приведено |
Подготовка: права, контекст исполнения и менеджер записи
До кода проверьте три вещи.
- Права. У роли пользователя должно быть право «Изменение» (для интерактивного ввода — ещё «Интерактивное добавление») на регистр. Без них будет исключение о нарушении прав доступа.
- Контекст. Менеджеры и наборы записей живут только на сервере. В модуле формы код пишут в процедуре с директивой
&НаСервереили в общем серверном модуле. На клиентеРегистрыСведенийнедоступен. - Режим записи регистра. В подчинённый регистр менеджер записи вы не создадите — у него есть только набор записей с отбором по регистратору. Если
СоздатьМенеджерЗаписи()не находится, регистр не независимый.
Добавить запись вручную, без программирования
Если нужна одна-две записи, код не нужен:
- Включите команду «Все функции»: главное меню (☰) →
Сервис→Параметры→ флажок «Отображать команду “Все функции”». - Главное меню →
Все функции→ веткаРегистры сведений→ нужный регистр. - В форме списка нажмите «Создать» (или
Ins). - Заполните период, измерения, ресурсы и нажмите «Записать и закрыть».
Форма записи работает через тот же менеджер записи. Если запись с таким ключом уже есть, форма откажет и сообщит о дубле — в отличие от кода, где по умолчанию стоит замещение.
Создание менеджера записи
Менеджер записи — объект для работы с одной записью регистра. Создаётся одной строкой:
МенеджерЗаписи = РегистрыСведений.ПлановыеЦены.СоздатьМенеджерЗаписи();
Здесь и дальше ПлановыеЦены — пример регистра: периодический «в пределах дня», измерения Номенклатура и Склад, ресурс Цена, реквизит Комментарий. Замените имена на свои.
Заполнение периода, измерений и ресурсов записи
Поля менеджера записи называются так же, как реквизиты регистра в конфигураторе. Полный пример добавления записи в независимый регистр сведений:
&НаСервере
Процедура ДобавитьЦенуНаСервере(Номенклатура, Склад, Цена)
МенеджерЗаписи = РегистрыСведений.ПлановыеЦены.СоздатьМенеджерЗаписи();
// Ключ записи
МенеджерЗаписи.Период = НачалоДня(ТекущаяДатаСеанса());
МенеджерЗаписи.Номенклатура = Номенклатура;
МенеджерЗаписи.Склад = Склад;
// Ресурсы и реквизиты
МенеджерЗаписи.Цена = Цена;
МенеджерЗаписи.Комментарий = "Загружено обработкой";
МенеджерЗаписи.Записать(); // Замещать = Истина по умолчанию
КонецПроцедуры
Что важно при заполнении:
- 🕐 Период берите от
ТекущаяДатаСеанса(), а не отТекущаяДата(). Первая учитывает часовой пояс сеанса, вторая берёт время компьютера, где выполняется код. - 🧩 Незаполненное измерение — допустимая часть ключа. Запись «Номенклатура = Х, Склад = пусто» — отдельная запись, а не «цена для всех складов». Если такое не нужно, включите «Запрет незаполненных значений».
- 🔢 Тип ресурса. Число с длиной 15 и точностью 2 обрежет 10,555 до 10,56 по правилам округления. Проверьте точность заранее.
- 📝 Реквизиты заполняются как ресурсы, но в отборах среза последних не участвуют.
Чтобы заполнить поля из структуры или строки таблицы, удобен ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Источник). Он переносит значения только для совпадающих по имени полей.
Методы записи в регистр: менеджер или набор записей
Есть два способа программно записать данные. Выбор зависит от количества записей и от того, что делать с уже существующими.
| Способ | Когда | Риск |
|---|---|---|
МенеджерЗаписи.Записать(Истина) | Одна запись, перезапись допустима | Тихо затрёт запись с тем же ключом |
МенеджерЗаписи.Записать(Ложь) | Одна запись, дубль недопустим | Исключение, если ключ уже есть |
НаборЗаписей.Записать(Истина) с отбором | Пакет записей с заменой «среза» | Удалит всё, что попало в отбор и нет в наборе |
НаборЗаписей.Записать(Ложь) | Пакет только новых записей | Весь набор не запишется при первом дубле |
Набор записей с отбором
Набор записей — коллекция записей, ограниченная отбором. При Записать(Истина) платформа удаляет из базы всё, что соответствует отбору, и пишет содержимое набора.
НаборЗаписей = РегистрыСведений.ПлановыеЦены.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Период.Установить(НачалоДня(ТекущаяДатаСеанса()));
НаборЗаписей.Отбор.Номенклатура.Установить(Номенклатура);
НаборЗаписей.Отбор.Склад.Установить(Склад);
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = НачалоДня(ТекущаяДатаСеанса());
НоваяЗапись.Номенклатура = Номенклатура;
НоваяЗапись.Склад = Склад;
НоваяЗапись.Цена = Цена;
НаборЗаписей.Записать(); // Замещение = Истина
Значения полей каждой записи должны совпадать с отбором. Отбор не заполняет их сам: если оставить Склад пустым при отборе по складу, платформа откажет в записи.
Набор без отбора и Записать(Истина) удаляет ВСЕ записи регистра и оставляет только то, что лежит в наборе. Так теряют справочные данные целиком. Перед первым запуском такой обработки сделайте резервную копию базы и проверьте её на копии.
Как дописать запись, ничего не удаляя
Если нужно именно добавить, а существующие записи не трогать, используйте Записать(Ложь) — у менеджера или у набора. При конфликте ключа вы получите исключение, а не тихую замену. Это честнее: ошибку видно, данные целы.
Удаление и изменение
Для полноты: тот же менеджер записи умеет читать и удалять. Заполните ключ, вызовите Прочитать(), проверьте Выбран(), затем измените ресурс и Записать() или вызовите Удалить(). Набор удаляет записи так: установили отбор, ничего не добавили, Записать().
Ошибки при записи и перехват исключений
Запись в регистр — операция с базой, она может упасть. Оборачивайте её в Попытка … Исключение и пишите причину в журнал регистрации, а не только пользователю на экран.
Попытка
МенеджерЗаписи.Записать(Ложь);
Исключение
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Загрузка плановых цен",
УровеньЖурналаРегистрации.Ошибка,
Метаданные.РегистрыСведений.ПлановыеЦены, , ТекстОшибки);
ВызватьИсключение КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
Если внутри одной операции пишете в несколько регистров, оборачивайте всё в транзакцию: НачатьТранзакцию(), затем ЗафиксироватьТранзакцию() в конце блока Попытка и ОтменитьТранзакцию() в Исключение. Тогда либо записалось всё, либо ничего.
Проверка уникальности и актуальности перед добавлением записи
Иногда нужно добавить запись в регистр, только если значение изменилось. Иначе история заполняется одинаковыми ценами каждый день.
Есть ли уже запись с таким ключом
Самый надёжный способ — запрос по точному ключу:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ПлановыеЦены.Цена
|ИЗ
| РегистрСведений.ПлановыеЦены КАК ПлановыеЦены
|ГДЕ
| ПлановыеЦены.Период = &Период
| И ПлановыеЦены.Номенклатура = &Номенклатура
| И ПлановыеЦены.Склад = &Склад";
Запрос.УстановитьПараметр("Период", НачалоДня(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", Склад);
ЗаписьЕсть = Не Запрос.Выполнить().Пустой();
Изменилось ли значение на дату
Для периодического регистра сравнивают новое значение с последним действующим. Короткий путь — метод менеджера регистра ПолучитьПоследнее():
Отбор = Новый Структура("Номенклатура, Склад", Номенклатура, Склад);
Последнее = РегистрыСведений.ПлановыеЦены.ПолучитьПоследнее(ТекущаяДатаСеанса(), Отбор);
Если Последнее.Цена <> Цена Тогда
// пишем новую запись
КонецЕсли;
Метод возвращает структуру ресурсов. Если записей нет, ресурсы будут пустыми (для числа — 0). Для пакетной проверки сотен позиций используйте в запросе виртуальную таблицу СрезПоследних(&Дата, Склад = &Склад) — один запрос вместо сотни обращений.
Чтение и запись в одной транзакции
Если между проверкой и записью другой сеанс успеет записать то же самое, проверка бесполезна. Ставьте управляемую блокировку до чтения:
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПлановыеЦены");
ЭлементБлокировки.УстановитьЗначение("Номенклатура", Номенклатура);
ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// проверка запросом и запись
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Массовая загрузка записей в регистр сведений без тормозов
Загрузка 50 000 цен через 50 000 менеджеров записи — это 50 000 отдельных обращений к базе. Набор записей пишет пачку за одно обращение и работает заметно быстрее.
Приёмы, которые дают основной выигрыш:
- Один набор на срез. Сгруппируйте данные по периоду. Для каждой даты — набор с отбором только по
Период, заполненный всеми строками этой даты, иЗаписать(Истина). Срез за дату заменится целиком, остальные даты не пострадают. Загрузить()вместо цикла. Если данные уже в таблице значений с колонками, названными как поля регистра, вызовитеНаборЗаписей.Загрузить(Таблица).- Убрать дубли до записи. Две строки с одним ключом внутри набора дадут ошибку всей пачки. Сверните таблицу:
Таблица.Свернуть("Период, Номенклатура, Склад", "Цена")— но помните, что ресурс при этом суммируется. Если суммировать нельзя, удалите дубли отдельно. - Режим загрузки.
НаборЗаписей.ОбменДанными.Загрузка = Истинаотключает обработчикиПередЗаписьюиПриЗаписимодуля набора. Используйте, только если уверены в чистоте данных: проверки из этих обработчиков тоже не выполнятся. - Пачки транзакций. Не пишите всё в одной транзакции на 50 000 строк — она долго держит блокировки. Фиксируйте каждые 1000–5000 записей.
Пример загрузки среза за одну дату:
НаборЗаписей = РегистрыСведений.ПлановыеЦены.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Период.Установить(ДатаЦен);
НаборЗаписей.ОбменДанными.Загрузка = Истина;
Для Каждого СтрокаЦены Из ТаблицаЦен Цикл
НоваяЗапись = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, СтрокаЦены);
НоваяЗапись.Период = ДатаЦен;
КонецЦикла;
НаборЗаписей.Записать(Истина);
Поле Период заполняется в цикле явно. Если в таблице есть колонка Период с другой датой, ЗаполнитьЗначенияСвойств перенесёт её, и строка не попадёт в отбор.
Частые вопросы
Можно ли добавить запись в регистр сведений с клиента?
&НаСервере или функцию общего модуля с флагом «Вызов сервера».Чем менеджер записи отличается от набора записей?
Почему вместо 17-го числа в регистре стоит 1-е?
Как добавить запись в непериодический регистр?
Период. Ключ записи состоит из измерений, отбор набора — тоже только по измерениям.