Набор записей регистра в 1С: отбор, чтение и запись
Содержание
Набор записей — это объект, через который код читает и пишет строки регистра пачкой, а не по одной. Создаётся методом СоздатьНаборЗаписей() у нужного регистра, например РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей(). Дальше вы ставите отбор, читаете существующие записи, меняете их и записываете обратно.
Понадобится он, когда нужно массово добавить, поправить или удалить движения регистра из обработки: загрузить курсы валют, пересчитать цены, исправить остатки
, перенести данные из другой базы. Ниже — как получить набор для разного типа регистров, чем отличается Записать(Истина) от Записать(Ложь) и где чаще всего теряют данные.
Что хранит набор записей и когда он нужен
Набор записей держит строки одного регистра, отобранные по заданному условию. Вы работаете не с отдельной строкой, а со всей пачкой: прочитали, изменили в памяти, записали целиком.
Типовые задачи, где без набора не обойтись:
- 📥 загрузка данных из файла или внешней системы в регистр сведений ;
- 🔁 массовая корректировка движений без ручного перепроведения документов;
- 🧹 удаление записей по условию (например, устаревших настроек);
- 🔧 разовое исправление остатков, когда перепровести документ нельзя.
А вот для простого чтения набор не нужен. Остатки регистра накопления, последние значения регистра сведений, обороты — всё это быстрее и правильнее брать запросом через виртуальные таблицы. Набор перебором строк для подсчёта остатка — классическая ошибка новичка.
Набор всегда работает по отбору. Отбор — это набор условий по полям регистра (измерениям, регистратору, периоду), которые определяют, какие именно записи попадут в выборку при чтении и какие будут заменены при записи с замещением. Пустой отбор означает «весь регистр», и именно поэтому с ним нужна осторожность.
Какие бывают регистры и как устроены их наборы
Набор всегда привязан к конкретному виду регистра. Менеджер зависит от типа:
| Вид регистра | Менеджер в коде | Как хранятся записи |
|---|---|---|
| Сведений | РегистрыСведений | независимо или подчинён регистратору |
| Накопления | РегистрыНакопления | только подчинён регистратору |
| Бухгалтерии | РегистрыБухгалтерии | только подчинён регистратору |
| Расчёта | РегистрыРасчета | только подчинён регистратору |
Ключевое различие — режим записи регистра сведений. Он задаётся в конфигураторе в свойствах регистра:
- Независимый — записи существуют сами по себе, отбор делается по измерениям (валюта, организация, дата). Такие записи можно заполнять и из кода, и руками.
- Подчинён регистратору — записи рождаются при проведении документа, отбор идёт по полю
Регистратор. Регистры накопления, бухгалтерии и расчёта всегда такие.
От режима зависит и отбор, и то, кто имеет право менять данные. Для подчинённого регистра «хозяин» записей — документ, и обходить его стоит только осознанно.
У регистра сведений есть ещё одно свойство, которое влияет на отбор, — периодичность. Она может быть в пределах секунды, дня, месяца, квартала, года или отсутствовать вовсе. Если периодичность задана, в отборе появляется поле Период, и для одной комбинации измерений хранится по одной записи на каждый период. Непериодический регистр хранит ровно одну запись на набор значений измерений, поэтому поля Период у него нет.
Как получить набор записей в коде: порядок действий
Последовательность одинаковая для любого регистра, меняется только менеджер и состав отбора.
- Создайте набор у нужного регистра.
- Установите отбор — чем именно ограничиваем выборку.
- Прочитайте, если собираетесь менять существующие записи.
- Добавьте или поправьте строки в памяти.
- Запишите набор.
Пример для независимого регистра сведений «Курсы валют» — добавляем курс на дату:
Набор = РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей();
Набор.Отбор.Валюта.Установить(Валюта);
Набор.Отбор.Период.Установить(ДатаКурса);
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Период = ДатаКурса;
НоваяЗапись.Валюта = Валюта;
НоваяЗапись.Курс = 93.5;
НоваяЗапись.Кратность = 1;
Набор.Записать(Истина);
Метод Добавить() возвращает новую пустую запись, её поля вы заполняете вручную. Обращение Набор[0] даёт первую уже существующую запись, Набор.Количество() — сколько их всего.
Если нужно только дописать строки и ничего не затрагивать из уже существующего, читать набор перед этим не обязательно. Но тогда пишите с параметром Ложь — об этом ниже.
Для регистра накопления всё то же самое, меняется только менеджер и состав полей записи. Здесь у строки есть направление движения:
Набор = РегистрыНакопления.ВзаиморасчётыСКонтрагентами.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(КорректировкаДолга);
Движение = Набор.Добавить();
Движение.Период = Дата;
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Регистратор = КорректировкаДолга;
Движение.Контрагент = Контрагент;
Движение.Сумма = 1000;
Набор.Записать(Истина);
Если нужно не изменить набор, а перенести его целиком (например, в другую базу или в табличный документ для анализа), пригодятся Выгрузить() и Загрузить(). Первый отдаёт записи в таблицу значений, второй — принимает их обратно:
Таблица = Набор.Выгрузить(); // все строки -> таблица значений
// ... обработка строк таблицы ...
Набор.Загрузить(Таблица); // вернули строки в набор
Набор.Записать(Истина);
Отбор и замещение: как записать без потери данных
Самое важное в наборе — параметр метода Записать(<Замещение>). По умолчанию он равен Истина.
Записать(Истина)— замещение. Платформа сначала удаляет все записи регистра, попадающие под установленный отбор, а потом пишет те, что лежат в наборе.Записать(Ложь)— добавление. Существующие записи остаются, набор дописывается сверху (для независимого регистра сведений — с контролем уникальности по измерениям).
Для регистров, подчинённых регистратору, отбор ставится по документу:
Набор = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();
// ... меняем строки ...
Набор.Записать(Истина); // заменяем все движения этого документа
Здесь Записать(Истина) трогает движения только указанного регистратора — потому что отбор ограничен им. Это безопасно.
Опасно: Записать(Истина) без установленного отбора на независимом регистре заменит ВЕСЬ регистр содержимым набора. Если набор при этом пустой — регистр будет очищен полностью. Всегда проверяйте, что отбор задан, прежде чем писать с замещением.
Чтобы удалить записи по условию, читать их необязательно: ставим отбор, оставляем набор пустым и пишем с замещением.
Набор = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей();
Набор.Отбор.Пользователь.Установить(УдаляемыйПользователь);
Набор.Записать(Истина); // набор пуст -> записи пользователя удалены
Добавление записей вручную через форму
Записи независимого регистра сведений можно заводить и без кода — прямо в форме списка, если редактирование не запрещено разработчиком.
Откройте список регистра через Главное меню → Все функции → Регистры сведений и выберите нужный. Команда «Все функции» включается в Сервис → Параметры → Отображать команду "Все функции". В открывшемся списке работают обычные кнопки командной панели: создать новую запись, изменить, пометить на удаление.
Так удобно внести единичную настройку или проверить, что обработка записала данные правильно. А вот регистры, подчинённые регистратору (накопления, бухгалтерии, расчёта, а также подчинённый регистр сведений), вручную в списке не редактируются — их строки формирует документ при проведении. Чтобы поправить такие движения, перепроводят документ или временно включают ручную корректировку движений в самом документе.
Набор записей в модуле проведения документа
Чаще всего набор записей разработчик создаёт не напрямую, а через свойство Движения документа — это тоже наборы записей, по одному на каждый регистр, который документ двигает. Платформа сама подставляет отбор по регистратору и записывает их после обработки проведения.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения.ТоварыНаСкладах.Записывать = Истина;
Для Каждого СтрокаТовары Из Товары Цикл
Движение = Движения.ТоварыНаСкладах.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = СтрокаТовары.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = СтрокаТовары.Количество;
КонецЦикла;
КонецПроцедуры
Строку Записывать = Истина ставят один раз для каждого регистра — она говорит платформе записать набор по окончании процедуры. Отдельно вызывать Записать() при этом не нужно: движения уйдут в базу автоматически вместе с проведением документа.
Такой подход правильнее прямой правки регистра: движения всегда соответствуют документу, а при перепроведении пересчитываются заново. Прямой набор по регистратору оставляют для разовых исправлений, когда перепровести документ по какой-то причине нельзя.
Запрос или набор записей: что выбрать
Частая путаница — пытаться считать данные набором. Разделение простое:
- Запрос — чтение. Остатки и обороты регистра накопления берут из виртуальных таблицы
Остатки,Обороты,ОстаткиИОбороты; актуальные значения регистра сведений — изСрезПоследних. - Набор записей — изменение. Читать им стоит только то, что потом будете менять.
Пример чтения последнего курса запросом (а не перебором набора):
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Курс, Кратность
|ИЗ РегистрСведений.КурсыВалют.СрезПоследних(&Дата, Валюта = &Валюта)";
Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Запрос.УстановитьПараметр("Валюта", Валюта);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Курс = Выборка.Курс;
КонецЕсли;
Запрос с виртуальной таблицей отработает на порядок быстрее, чем чтение всего набора и поиск нужной строки в цикле. Набор же оставьте для записи результата.
То же касается регистра накопления: текущие остатки по складу берут из виртуальной таблицы Остатки, а не перебором набора записей. Набор хранит все движения — и приходы, и расходы, — и сам по себе остатка не считает. Виртуальные таблицы уже свёрнуты платформой, работают по итогам и отдают готовое число.
Практическое правило простое: если данные нужны для отображения, расчёта или условия — это запрос; если их нужно создать, поправить или удалить — это набор записей.
Типичные ошибки при работе с наборами
- Забыли
Прочитать()перед изменением. Набор создаётся пустым. Если сразу вызватьЗаписать(Истина), платформа заместит существующие записи пустым набором — данные по отбору пропадут. Читайте всегда, когда правите уже существующее. Записать(Истина)без отбора на независимом регистре. Платформа понимает это как «замести весь регистр». Отбор — обязателен.- Правка движений в обход документа. Если изменить набор подчинённого регистра напрямую, движения перестанут соответствовать проведённому документу: перепроведение всё вернёт обратно, а до него отчёты покажут одно, а документ — другое. Для разовой корректировки используйте ручную корректировку движений в документе, а не скрытую правку регистра.
- Запись по одной строке в цикле. Набор потому и называется набором, что пишется целиком. Собирайте все строки в памяти и вызывайте
Записать()один раз. - Нет блокировки при параллельной работе. Если набор читают и пишут несколько сеансов, оборачивайте операцию в транзакцию и управляемую блокировку, иначе можно затереть чужие изменения.
- Две записи с одинаковым набором измерений. В независимом регистре сведений комбинация измерений (и периода, если он есть) уникальна. Если в набор попадут две строки с одинаковым ключом, запись завершится ошибкой. Перед загрузкой из внешних данных сворачивайте дубли по измерениям.
- Запись без заполненного периода. Для периодического регистра сведений поле
Периодобязательно к заполнению у каждой строки — иначе платформа не поймёт, к какому моменту относится запись.
Массовое изменение движений в рабочей базе — операция с риском. Сначала проверьте код на копии, а серьёзные правки делайте в период без активных пользователей: перепроведение и блокировки регистров тормозят остальных.
Отдельно про транзакцию. Когда в цикле записывается много наборов, каждая запись по умолчанию открывает и закрывает свою транзакцию — это медленно и не защищает от частичной загрузки при сбое. Правильнее открыть одну транзакцию на всю пачку: если на середине что-то упадёт, откатится всё разом, а не половина.
НачатьТранзакцию();
Попытка
Для Каждого Строка Из ДанныеЗагрузки Цикл
Набор = РегистрыСведений.Цены.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(Строка.Номенклатура);
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Номенклатура = Строка.Номенклатура;
НоваяЗапись.Цена = Строка.Цена;
Набор.Записать(Истина);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Частые вопросы о наборах записей
Чем набор записей отличается от запроса?
Можно ли поправить движения проведённого документа набором записей?
Прочитать(), правка, Записать(Истина). Но при следующем перепроведении документ перезапишет движения по своему алгоритму, и ваши правки исчезнут. Для устойчивого результата включите в документе ручную корректировку движений.Как удалить все записи регистра по условию?
Записать(Истина). Пустой набор с замещением удаляет записи, попавшие под отбор.Почему после записи данные пропали?
Записать(Истина) при неустановленном или слишком широком отборе, либо запись без предварительного Прочитать(). Замещение удаляет всё, что подходит под отбор, перед тем как писать набор.Нужна ли транзакция?
НачатьТранзакцию() / ЗафиксироватьТранзакцию() и ставьте управляемую блокировку — так и быстрее, и безопаснее при параллельной работе.