Как исправить ошибку «Запись не верна! Значение поля не соответствует установленному отбору» в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

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

Выясняем причину: почему платформа 1С выдает эту ошибку?

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

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

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

Посмотрим на исходный код автора, который привел к ошибке:


НаборЗаписей = РегистрыСведений.Пакет.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Пакет.Установить(Пакет);
НаборЗаписей.Прочитать();
Для каждого ЗаписьНабора из НаборЗаписей Цикл
    // Ошибка происходит здесь: мы меняем ключевое поле, по которому стоит отбор!
    ЗаписьНабора.Пакет      = Документы.Пакет.ПустаяСсылка();
    ЗаписьНабора.Распечатан = Ложь;
КонецЦикла;
НаборЗаписей.Записать(Истина);

Анализируем решение автора и его скрытые недостатки

Автор темы нашел обходной путь. Он перенес отбор на другое измерение регистра — РеализацияТиУ, значение которого в процессе обработки не изменяется. Код стал выглядеть следующим образом:


НаборЗаписей = РегистрыСведений.Пакет.СоздатьНаборЗаписей(); 
НаборЗаписей.Отбор.РеализацияТиУ.Установить(РеализацияТиУ); 
НаборЗаписей.Прочитать(); 
Для каждого ЗаписьНабора из НаборЗаписей Цикл 
    ЗаписьНабора.Пакет      = Документы.Пакет.ПустаяСсылка(); 
    ЗаписьНабора.Распечатан = Ложь; 
КонецЦикла; 
НаборЗаписей.Записать(Истина); 

Этот код действительно отработает без ошибок транзакции, поскольку отбор по измерению РеализацияТиУ остался неизменным, а измерение Пакет теперь можно безопасно модифицировать внутри набора. Однако такое решение имеет серьезные архитектурные недостатки, о которых необходимо знать:

Рассмотрим более надежные, производительные и безопасные способы решения этой задачи, соответствующие стандартам разработки фирмы «1С».

Способ А. Использование Менеджера записи (для точечных изменений)

Если перед нами стоит задача изменить измерение у одной конкретной записи регистра, использование тяжелого объекта НаборЗаписей нецелесообразно. Гораздо эффективнее применить объект РегистрСведенийМенеджерЗаписи.

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

  1. Инициализируем менеджер записи.
  2. Заполняем все ключевые поля (измерения) старыми значениями.
  3. Считываем запись из базы данных с помощью метода Прочитать().
  4. Если запись существует, физически удаляем ее методом Удалить().
  5. Присваиваем ключевым полям новые значения.
  6. Записываем объект методом Записать().

Проанализируем практический пример реализации этого алгоритма:


// Создаем менеджер записи для точечной работы
МенеджерЗаписи = РегистрыСведений.Пакет.СоздатьМенеджерЗаписи();
МенеджерЗаписи.РеализацияТиУ = ТекущаяРеализация;
МенеджерЗаписи.Пакет        = СтарыйПакет; // Заполняем старое значение ключа

МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
    // Удаляем старую запись из базы данных, освобождая ключ уникальности
    МенеджерЗаписи.Удалить();
    
    // Заполняем новые значения измерений и ресурсов
    МенеджерЗаписи.РеализацияТиУ = ТекущаяРеализация;
    МенеджерЗаписи.Пакет        = Документы.Пакет.ПустаяСсылка();
    МенеджерЗаписи.Распечатан   = Ложь;
    
    // Фиксируем новую запись в базе данных
    МенеджерЗаписи.Записать();
КонецЕсли;

Способ Б. Метод двух наборов записей (для массовой обработки данных)

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

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

Разберем по шагам, как это реализовать на практике:


// ШАГ 1: Считываем старые данные, которые необходимо скорректировать
НаборСтарый = РегистрыСведений.Пакет.СоздатьНаборЗаписей();
НаборСтарый.Отбор.Пакет.Установить(СтарыйПакет);
НаборСтарый.Прочитать();

// Выгружаем считанные записи во временную таблицу значений в памяти
ТаблицаДанных = НаборСтарый.Выгрузить();

// Удаляем старые записи из базы данных: записываем пустой набор
НаборСтарый.Очистить();
НаборСтарый.Записать(Истина);

// ШАГ 2: Подготавливаем новый набор записей для записи обновленных данных
НаборНовый = РегистрыСведений.Пакет.СоздатьНаборЗаписей();
НаборНовый.Отбор.Пакет.Установить(Документы.Пакет.ПустаяСсылка());
НаборНовый.Прочитать(); // Заполняем набор существующими записями с пустым пакетом (чтобы не затереть их)

// Переносим данные из временной таблицы, попутно изменяя ключевое поле
Для Каждого СтрокаТаблицы Из ТаблицаДанных Цикл
    НоваяСтрока = НаборНовый.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТаблицы);
    
    // Корректируем измерение и ресурс непосредственно в новой строке набора
    НоваяСтрока.Пакет      = Документы.Пакет.ПустаяСсылка();
    НоваяСтрока.Распечатан = Ложь;
КонецЦикла;

// Записываем обновленный набор данных в базу
НаборНовый.Записать(Истина);

Данный подход гарантирует высокую производительность, так как все изменения производятся пакетами в оперативной памяти сервера приложений, а блокировки на СУБД накладываются на минимально возможные промежутки времени.

Другие ситуации, вызывающие ошибку несоответствия отбору

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

Используя предложенные методики (точечный менеджер записи для единичных строк и двухэтапную перезапись наборов для массовых изменений), вы сможете создавать надежный, быстродействующий и отказоустойчивый код для работы с базами данных 1С.

← На главную