Как исправить ошибку «Значение поля не соответствует установленному отбору» при программном заполнении измерения регистра сведений

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

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

Суть проблемы и механизм работы наборов записей

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

Проанализируем, что происходит на уровне платформы. Когда мы работаем с объектом РегистрСведенийНаборЗаписей, платформа накладывает жесткое ограничение: значения всех измерений в каждой строке набора должны строго соответствовать значениям, установленным в свойстве Отбор этого набора. Если в отборе указано, что Номенклатура — это «Кирпич», а вы попытаетесь записать в наборе строку с номенклатурой «Цемент», система выдаст ошибку. То же самое происходит, когда в отборе стоит пустое значение (например, пустая строка или пустой UUID), а вы программно подставляете в запись новое значение.

Почему ошибка возникает именно в форме списка?

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

Когда управление передается в обработчик ПередЗаписью модуля набора записей, мы имеем дело с объектом, у которого уже заполнен Отбор. Изменяя значение измерения в цикле по записям набора, мы создаем конфликт: в записи значение стало новым, а в объекте Отбор осталось старым. Это и приводит к исключению.

Решение 1: Синхронизация отбора в модуле набора записей

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


Процедура ПередЗаписью(Отказ, Замещение)
    
    // Проверяем, что мы не находимся в процессе обмена данными
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Для Каждого ТекЗапись Из ЭтотОбъект Цикл
        
        // Генерируем новый идентификатор, если он еще не заполнен
        Если ТекЗапись.УникальныйИдентификаторЗаписи = "" Тогда
            НовыйУИД = Строка(Новый УникальныйИдентификатор());
            ТекЗапись.УникальныйИдентификаторЗаписи = НовыйУИД;
            
            // Ключевой момент: синхронизируем отбор
            // Если в отборе установлено пустое значение, меняем его на наш новый УИД
            Если ЭтотОбъект.Отбор.УникальныйИдентификаторЗаписи.Установлен 
                И ЭтотОбъект.Отбор.УникальныйИдентификаторЗаписи.Значение <> НовыйУИД Тогда
                
                ЭтотОбъект.Отбор.УникальныйИдентификаторЗаписи.Значение = НовыйУИД;
                ЭтотОбъект.Отбор.УникальныйИдентификаторЗаписи.Использование = Истина;
                
            КонецЕсли;
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

Важное примечание: Этот метод подходит, если набор всегда содержит только одну запись (что типично для правки в форме списка). Если же набор содержит множество записей с разными значениями этого измерения, такой подход не сработает, так как Отбор может содержать только одно конкретное значение для каждого измерения.

Решение 2: Использование событий формы или заполнения

Разберем более «чистый» с точки зрения архитектуры подход. Чтобы избежать конфликтов с отборами на уровне набора записей, лучше инициализировать уникальные идентификаторы до того, как начнется процесс записи набора. Для этого можно использовать обработчик ПриУстановкеНовыхДанныхЗаполнения в модуле менеджера регистра сведений или события формы.

Если мы работаем в управляемой форме, мы можем генерировать значение при создании записи:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Если Объект.УникальныйИдентификаторЗаписи = "" Тогда
        Объект.УникальныйИдентификаторЗаписи = Строка(Новый УникальныйИдентификатор());
    КонецЕсли;
    
КонецПроцедуры

В этом случае при нажатии кнопки «Записать» платформа сформирует набор записей, в отборе которого уже будет стоять корректный, заранее сгенерированный идентификатор, и ошибка не возникнет.

Анализ использования UUID в измерениях

Посмотрим на ситуацию шире: зачем вообще использовать УникальныйИдентификатор как измерение? Обычно это делается для того, чтобы обойти ограничение регистра на уникальность ключа (комбинации измерений). Если для одной номенклатуры нужно хранить несколько записей, добавление UUID в измерения позволяет «размножить» их.

Однако стоит помнить о следующих моментах:

  1. Производительность: Большое количество уникальных идентификаторов в индексах таблиц СУБД может приводить к сильной фрагментации индексов и замедлению работы при больших объемах данных.
  2. Удобство работы: Если вам нужно будет программно изменить или удалить конкретную запись, вам придется всегда знать этот идентификатор. Проанализируйте, возможно, вместо UUID лучше использовать измерение с типом Дата (если важна хронология) или Число (номер строки).
  3. Чтение данных: Как отмечалось в обсуждении, «запись — ничто, чтение — всё». Если ваша цель — простота получения данных через запросы, то регистр сведений действительно удобнее табличных частей справочников, но архитектура ключа должна быть продуманной.

Рекомендации по работе с наборами записей

Рассмотрим еще один важный аспект — параметр Замещение. По умолчанию метод Записать() вызывается с параметром Замещение = Истина. Это означает, что платформа сначала удаляет из базы данных все записи, которые соответствуют текущему отбору набора, а затем записывает новые строки из коллекции набора.

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

Подведем итог: ошибка соответствия отбору — это защитный механизм платформы, предотвращающий запись «мусорных» данных, которые невозможно будет найти по установленному ключу. Решая эту проблему, выбирайте между синхронизацией отбора в модуле набора или (что предпочтительнее) предварительным заполнением данных в форме или при инициализации новой записи.

← На главную