Ошибка «Запись не верна, не соответствует по отбору» в 1С: причины и способы решения

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

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

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

Суть проблемы: Несоответствие типов данных

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

Рассмотрим фрагмент кода, который вызывает ошибку:


НаборЗаписей.Отбор.Организация.Установить(строка.Организация); 
НаборЗаписей.Отбор.УникальныйИдентификатор.Установить(строка.УникальныйИдентификатор); 
НаборЗаписей.Прочитать(); 

Если НаборЗаписей.Количество() = 0 Тогда 
    НоваяЗапись = НаборЗаписей.Добавить(); 
    НоваяЗапись.Организация = Спр.Организации.НайтиПоНаименованию(строка.Организация); 
    // Дальнейшее заполнение полей...
    НаборЗаписей.Записать(); 
КонецЕсли;

Проанализируем этот код. В первой строке метод Установить() принимает переменную строка.Организация. Если это данные, полученные, например, через веб-сервис или из текстового файла, то тип этой переменной — Строка. Однако в метаданных регистра измерение Организация имеет тип СправочникСсылка.Организации.

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

Решение проблемы: Приведение типов в отборе

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

Исправленный вариант установки отбора должен выглядеть следующим образом:


// Сначала находим ссылку на объект
СсылкаОрганизация = Справочники.Организации.НайтиПоНаименованию(строка.Организация);

// Устанавливаем отбор, передавая именно ССЫЛКУ
НаборЗаписей.Отбор.Организация.Установить(СсылкаОрганизация);
НаборЗаписей.Отбор.УникальныйИдентификатор.Установить(строка.УникальныйИдентификатор);

НаборЗаписей.Прочитать();
// ... дальнейшая логика заполнения и записи

Теперь значение в НаборЗаписей.Отбор.Организация.Значение и значение в НоваяЗапись.Организация совпадают по типу и по содержанию. Ошибка исчезнет.

Дополнительные причины возникновения ошибки

Помимо несовпадения типов (строка vs ссылка), существуют и другие сценарии, приводящие к данной ошибке:

  1. Изменение измерения внутри цикла: Если вы установили отбор по организации «Альфа», а в коде при заполнении записи написали НоваяЗапись.Организация = Справочники.Организации.НайтиПоНаименованию("Бета"), запись не будет произведена.
  2. Неявные отборы по регистратору: Для регистра сведений с режимом записи «Подчинение регистратору» отбор по полю Регистратор является обязательным. Попытка записать в такой набор строку, где поле Регистратор отличается от установленного в отборе, вызовет аналогичный сбой.
  3. Логические ошибки в свойстве «Использование»: Метод Установить() автоматически включает свойство Использование в значение Истина. Однако если вы манипулируете отбором вручную через свойства Значение и Использование, убедитесь, что флаг использования активен.

Правильная проверка на существование объекта («Битые ссылки»)

В ходе обсуждения был затронут вопрос проверки на «пустую» или «битую» ссылку, когда объект не найден в базе. Часто предлагается использовать строковое сравнение: Лев(Строка(Ссылка), 17) = "<Объект не найден".

Мы настоятельно рекомендуем не использовать этот метод, так как он зависит от языка платформы и крайне медленно работает. Проанализируем более профессиональные способы проверки:

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

Оптимизация производительности: работа с поиском

Посмотрим на ситуацию шире. Вызов метода НайтиПоНаименованию() внутри цикла (особенно если вы загружаете данные из внешней системы) — это «антипаттерн» программирования в 1С. Каждый такой вызов провоцирует отдельный запрос к серверу базы данных.

Как сделать правильно:

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

Альтернативный подход: Менеджер записи

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

В отличие от набора записей, менеджер записи:

Пример использования менеджера записи:


МенеджерЗаписи = РегистрыСведений.ВашРегистр.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Организация = НужнаяСсылка;
МенеджерЗаписи.УникальныйИдентификатор = НужныйУИД;
МенеджерЗаписи.Прочитать();

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

МенеджерЗаписи.Записать();

Этот подход более декларативен и часто помогает избежать ошибок типизации, с которых началось наше исследование.

← На главную