Как программно изменить одно поле в регистре сведений 1С?

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

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

Почему удаляются записи при попытке их изменения?

Проанализируем ситуацию. Если при использовании объекта НаборЗаписей записи пропадают, это чаще всего связано с неправильным пониманием работы ключей (измерений) регистра и отборов. Разберем основные причины:

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

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

Режим подчиненности регистра: Подчиненный регистратору или Независимый

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

  1. Регистр, подчиненный регистратору. Для таких регистров изменение записей возможно только через объект НаборЗаписей. При этом отбор обязательно должен устанавливаться по полю Регистратор.
  2. Независимый регистр. Здесь мы свободны в выборе и можем использовать как НаборЗаписей, так и МенеджерЗаписи. Для точечного изменения одиночных строк независимого регистра лучше всего подходит именно менеджер.

Способ 1: Использование Менеджера Записи (для независимых регистров)

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

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


// Создаем менеджер записи для независимого регистра
Менеджер = РегистрыСведений.ПрикрепленныеПациентыСоглашений.СоздатьМенеджерЗаписи();

// Обязательно устанавливаем ВСЕ измерения, идентифицирующие уникальную запись
Менеджер.Соглашение = ТекСоглашение;
Менеджер.Пациент = ТекПациент;

// Читаем запись из базы
Менеджер.Прочитать();

// Проверяем, нашлась ли такая запись
Если Менеджер.Выбран() Тогда
    // Изменяем нужный реквизит или ресурс
    Менеджер.Полис = ТекПолис;
    
    // Записываем изменения
    Менеджер.Записать();
КонецЕсли;

Важное замечание: Если вы прочитали независимую запись через менеджер, изменили у нее Измерение и нажали Записать(), система создаст новую запись. Старая при этом останется в базе! Чтобы избежать дублей, старую запись нужно явно удалить методом Менеджер.Удалить(), а уже затем создавать новую с новым ключом.

Способ 2: Использование Набора Записей (для регистров, подчиненных регистратору)

Если наш регистр подчинен документу-регистратору, использовать менеджер записи нельзя. Нам придется работать через набор движений самого документа. Рассмотрим пример изменения записей в подчиненном регистре:


&НаСервере
Процедура ИзменитьЗаписиПодчиненногоРегистра()
    // Получаем ссылку на документ-регистратор
    УстановкаЦенСсылка = Документы.УстановкаЦен.НайтиПоНомеру("ПТО-00000001", '20201231');
    УстановкаЦен = УстановкаЦенСсылка.ПолучитьОбъект();
    
    // Получаем набор записей по конкретному регистру из движений документа
    НаборЗаписей = УстановкаЦен.Движения.ЦеныРеализации;
    
    // Отбор по регистратору устанавливается автоматически через коллекцию движений
    НаборЗаписей.Прочитать();
    
    Для Каждого Запись Из НаборЗаписей Цикл
        // Изменяем ресурс
        Запись.Цена = Запись.Цена * 1.3; // Увеличиваем цену на 30%
    КонецЦикла;
    
    // Добавление новой записи в этот же набор (при необходимости)
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Период = ТекущаяДата();
    НоваяЗапись.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Молоко 3,2%");
    НоваяЗапись.Цена = 3;
    
    // Записываем измененный набор в базу (Истина - с замещением)
    НаборЗаписей.Записать(Истина);
КонецПроцедуры

Оптимизация массовой обработки записей

Если требуется исправить не одну, а 1000 записей, типичной ошибкой начинающих специалистов является использование методов НайтиПоНаименованию или выполнение запросов внутри цикла обхода. Это катастрофически снижает производительность. Для пакетного обновления данных без написания кода можно использовать помощник массового изменения значений в регистрах.

Как сделать правильно? Выясним причину тормозов: обращения к базе в цикле. Решение: выполним один общий Запрос, который соберет все ключи записей и новые значения. Никаких поисков по строке в коде быть не должно — все связи делаются соединениями таблиц внутри самого запроса.

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


// 1. Формируем один запрос для получения всех необходимых данных
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Рег.Соглашение КАК Соглашение,
    |    Рег.Пациент КАК Пациент,
    |    НовыеДанные.НовыйПолис КАК НовыйПолис
    |ИЗ
    |    РегистрСведений.ПрикрепленныеПациентыСоглашений КАК Рег
    |    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НовыеДанные КАК НовыеДанные
    |    ПО ...";

Выборка = Запрос.Выполнить().Выбрать();

// 2. Обходим выборку и точечно обновляем записи
Пока Выборка.Следующий() Цикл
    Менеджер = РегистрыСведений.ПрикрепленныеПациентыСоглашений.СоздатьМенеджерЗаписи();
    Менеджер.Соглашение = Выборка.Соглашение;
    Менеджер.Пациент = Выборка.Пациент;
    Менеджер.Прочитать();
    
    Если Менеджер.Выбран() Тогда
        Менеджер.Полис = Выборка.НовыйПолис;
        Менеджер.Записать();
    КонецЕсли;
КонецЦикла;

Отключение логики проверок при служебных корректировках

Иногда записи регистра нужно изменить разово (например, при исправлении системной ошибки), а на регистре «висят» тяжелые подписки на события: обмен данными, дополнительные проверки, логирование. Чтобы ускорить запись и обезопасить базу от лишних срабатываний, мы можем отключить эти обработчики.

Перед вызовом метода Записать() у менеджера или набора установим специальный флаг загрузки:


НаборЗаписей.ОбменДанными.Загрузка = Истина;
// или
Менеджер.ОбменДанными.Загрузка = Истина;

Это укажет платформе 1С записать данные «как есть», минуя большинство стандартных проверок в модуле набора записей и подписках. Применяйте этот флаг осторожно и только для служебных разовых обработок, чтобы не нарушить ссылочную целостность или логику системы. Предварительная настройка автоматического архивирования и тестирования баз 1С обеспечит сохранность данных при возникновении критических ошибок в ходе корректировки.

← На главную