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