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