Как добавить или обновить записи в регистре сведений, не удаляя остальные данные?

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

Часто при разработке в 1С мы сталкиваемся с задачей: необходимо программно добавить новые записи в РегистрСведений или обновить уже существующие — для их быстрого ручного изменения без программирования подойдет универсальный редактор записей регистров для 1С. Начинающие разработчики часто пишут код, создающий НаборЗаписей, добавляют в него данные в цикле и вызывают метод Записать(). Но в таком случае происходит неприятная ситуация — все старые данные, которые были в регистре, бесследно удаляются. Попытки использовать Записать(Ложь) предотвращают очистку, но тогда система не позволяет изменить существующие записи. Давайте разберем по шагам, почему так происходит, и проанализируем правильные способы работы с регистрами сведений.

Постановка правильного отбора в наборе записей

Когда мы работаем с объектом НаборЗаписей, платформа 1С оперирует срезами данных, которые ограничены установленным отбором. Если мы не устанавливаем отбор, платформа считает, что мы хотим перезаписать весь регистр целиком. Именно поэтому вызов метода Записать() без отборов приводит к полному стиранию всех предыдущих записей.

Рассмотрим подробнее первое и самое главное правило: чтобы не затереть лишние данные, мы обязаны установить для набора записей правильный отбор по измерениям (или периоду, если регистр периодический). Важно помнить техническое ограничение: отбор в наборе записей регистра сведений может быть установлен только на точное равенство. Использовать диапазоны дат или условия "Больше/Меньше" не получится.

Полный алгоритм обновления через метод Прочитать()

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

  1. Создаем объект НаборЗаписей для нужного регистра.
  2. Устанавливаем значения отбора по всем ключевым измерениям, чтобы сузить область работы.
  3. Вызываем метод Прочитать(). В этот момент платформа загружает в набор те записи, которые уже существуют в базе по заданному отбору.
  4. Обходим прочитанный набор циклом, модифицируем нужные ресурсы у существующих строк или добавляем новые с помощью метода Добавить() прямо в этот набор.
  5. Вызываем метод Записать(). Таким образом, старые записи по узкому отбору заменяются на нашу модифицированную версию, а данные вне отбора остаются абсолютно нетронутыми.

Посмотрим на пример правильного кода с использованием чтения:


НаборЗаписей = РегистрыСведений.ДанныеПроизводственногоКалендаря.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ПроизводственныйКалендарь.Установить(СсылкаНаКалендарь);
НаборЗаписей.Отбор.Дата.Установить(ДеньГрафика);

// Считываем существующие записи по отбору
НаборЗаписей.Прочитать();

Если НаборЗаписей.Количество() = 0 Тогда
    // Записи нет, добавляем новую
    НоваяЗапись = НаборЗаписей.Добавить(); 
    НоваяЗапись.ПроизводственныйКалендарь = СсылкаНаКалендарь; 
    НоваяЗапись.Дата = ДеньГрафика; 
Иначе
    // Запись есть, берем первую (и единственную по полному ключу)
    НоваяЗапись = НаборЗаписей[0];
КонецЕсли;

НоваяЗапись.Часы = 8;
НаборЗаписей.Записать();

Использование Менеджера записи для одиночных операций

Если нам необходимо добавить или обновить только одну конкретную запись в независимом регистре сведений, использование наборов записей избыточно. Разберем более элегантный и методологически правильный вариант — использование объекта МенеджерЗаписи.

Создается он через вызов функции СоздатьМенеджерЗаписи() у нужного регистра. При работе с менеджером нам не нужно возиться с объектом Отбор. Достаточно просто заполнить значения всех измерений (и период для периодического регистра), установить ресурсы и выполнить запись.

Проанализируем пример кода с менеджером записи:


Менеджер = РегистрыСведений.ДанныеПроизводственногоКалендаря.СоздатьМенеджерЗаписи();
// Заполняем ключевые поля
Менеджер.ПроизводственныйКалендарь = СсылкаНаКалендарь;
Менеджер.Дата = ДеньГрафика;

// Заполняем ресурсы
Менеджер.Часы = 8;

// Выполняем сохранение
Менеджер.Записать();

Если запись с таким набором измерений уже есть в базе, система просто перезапишет ее ресурсы. Если записи нет — она будет создана с нуля. Соседние записи в регистре при этом гарантированно не пострадают.

Новые режимы замещения (начиная с платформы 8.3.26)

Современные версии платформы 1С предлагают еще более изящное решение проблемы. Выясним, что изменилось в версии 8.3.26. Теперь в метод Записать() объекта НаборЗаписей можно передавать специальное системное перечисление РежимЗамещения, а не просто логическое значение очистки.

Доступны новые режимы, например Обновление или Слияние. Если мы записываем набор в режиме Обновление, платформа самостоятельно ищет в базе существующие записи, ключевые поля которых совпадают с новыми строками в наборе. Система точечно обновляет ресурсы найденных записей и добавляет новые, при этом не удаляя остальной срез данных по регистру и не требуя предварительного использования метода Прочитать(). Это радикально ускоряет массовую загрузку или корректировку данных — для решения этой задачи есть обработка импорта и сопоставления данных в 1С.

Пример использования в новых версиях платформы:


// ... заполняем набор множеством новых записей
НаборЗаписей.Записать(РежимЗамещенияНабораЗаписей.Обновление);

Особенности регистров, подчиненных регистратору

В завершение разберем ситуацию, когда регистр сведений является подчиненным регистратору. В таких регистрах логика работы кардинально меняется. Мы не можем просто записать туда данные произвольно — каждая запись обязана ссылаться на конкретный документ-регистратор, который и породил эти "движения".

В этом случае при создании объекта НаборЗаписей отбор всегда должен устанавливаться по системному полю Регистратор. Любые манипуляции с добавлением, изменением или удалением происходят исключительно в рамках движений конкретного проведенного документа.

Посмотрим на пример установки отбора для такого регистра:


НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
// Обязательный отбор по регистратору
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
// При необходимости читаем старые движения
НаборЗаписей.Прочитать();
// ... добавляем или меняем записи
НаборЗаписей.Записать();

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

← На главную