Как проверить наличие записи в регистре сведений и обновить её, если меняется измерение

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

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

Понимание ключа записи и состава регистра

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

В рассматриваемом примере (регистр КодыТоваровПодключаемогоОборудованияOffline) измерениями являются ПравилоОбмена и Код, а Номенклатура выступает в роли ресурса. Это означает, что для одной номенклатуры может существовать несколько записей с разными кодами.

Способ 1: Использование Менеджера записи (RecordManager)

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


// Создаем менеджер записи для конкретного регистра
МенеджерЗаписи = РегистрыСведений.КодыТоваровПодключаемогоОборудованияOffline.СоздатьМенеджерЗаписи();

// Устанавливаем значения измерений (ключевых полей)
МенеджерЗаписи.ПравилоОбмена = ВыбПравило;
МенеджерЗаписи.Код          = ТекСтрока.SKU;

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

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

// Записываем результат. Параметр Истина (по умолчанию) означает замещение
МенеджерЗаписи.Записать();

Метод Выбран() возвращает Истина, если запись с указанными измерениями была успешно считана. Важно помнить, что если вы измените значение измерения после вызова Прочитать(), метод Записать() создаст новую запись, а старая останется в базе. Чтобы этого не произошло, необходимо использовать удаление.

Способ 2: Поиск существующей записи через запрос

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Регистр.ПравилоОбмена,
    |   Регистр.Код
    |ИЗ
    |   РегистрСведений.КодыТоваровПодключаемогоОборудованияOffline КАК Регистр
    |ГДЕ
    |   Регистр.Номенклатура = &Номенклатура";

Запрос.УстановитьПараметр("Номенклатура", НомСсылка);
РезультатЗапроса = Запрос.Выполнить();

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

Способ 3: Правильное изменение измерений через Набор записей

Выясним причину возникновения ошибки «Значение поля не соответствует установленному отбору». Она появляется, когда в объекте НаборЗаписей установлен отбор по одному значению измерения (например, Код = "107"), а в коде мы пытаемся присвоить этому полю другое значение (Запись.Код = "108").

Чтобы корректно изменить измерение, мы должны выполнить два шага: удалить старую запись и добавить новую. Это гарантирует отсутствие дублей. Рассмотрим правильный алгоритм работы с набором записей:


// 1. Удаляем старую запись
НаборУдаление = РегистрыСведений.КодыТоваровПодключаемогоОборудованияOffline.СоздатьНаборЗаписей();
НаборУдаление.Отбор.ПравилоОбмена.Установить(СтароеПравило);
НаборУдаление.Отбор.Код.Установить(СтарыйКод);
// Записываем пустой набор - это удаляет записи, попавшие в отбор
НаборУдаление.Записать();

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

НоваяЗапись = НаборЗапись.Добавить();
НоваяЗапись.ПравилоОбмена = НовоеПравило;
НоваяЗапись.Код          = НовыйКод;
НоваяЗапись.Номенклатура = СсылкаНаНоменклатуру;
НоваяЗапись.Используется = Истина;
НоваяЗапись.Активность   = Истина;

НаборЗапись.Записать();

Оптимизация: Пакетная запись (запись «пачкой»)

Рассмотрим ситуацию, когда данных много. Выполнение Записать() в цикле для каждой строки — плохая практика, так как это создает огромную нагрузку на базу данных. Проанализируем более эффективный подход:

  1. Сначала соберем все данные, которые нужно записать, в таблицу значений.
  2. Одним запросом выберем все существующие записи по списку номенклатуры.
  3. Для каждого уникального сочетания измерений сформируем набор записей.
  4. Выполним запись, желательно объединяя операции в одну транзакцию.

Для управления транзакциями используем встроенные функции 1С:


НачатьТранзакцию();
Попытка
    // Блок кода с удалением и добавлением записей
    // ...
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ЗаписьЖурналаРегистрации("Ошибка записи SKU", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;

Важные рекомендации

При реализации проверки на наличие записи обратите внимание на следующие моменты:

Индексирование: Если вы часто ищете записи по ресурсу (например, по номенклатуре), обязательно установите в свойствах этого ресурса в конфигураторе признак Индексировать. Это значительно ускорит выполнение поисковых запросов.

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

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

← На главную