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