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