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