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

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

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

Рассмотрим эту ситуацию на конкретном примере и разберем, почему она возникает и как её правильно решить.

Анализ проблемы: почему возникает ошибка?

Предположим, у нас есть задача добавить запись в непериодический независимый регистр сведений НаградыФизическихЛиц. Измерениями регистра являются ФизическоеЛицо и НомерПоПорядку. В регистре уже есть запись для сотрудника "Иванов Иван" с номером 1. Мы хотим добавить ему вторую награду с номером 2.

Первоначальный код может выглядеть так:


&НаСервере
Процедура ЗагрузитьНаградыФизЛиц_НаСервере()
    Сотрудник = Справочники.ФизическиеЛица.НайтиПоКоду("0000004372");
    
    МенеджерЗаписи = РегистрыСведений.НаградыФизическихЛиц.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.ФизическоеЛицо = Сотрудник;
    МенеджерЗаписи.НомерПоПорядку = 2; // Устанавливаем номер 2
    МенеджерЗаписи.Награда = Справочники.Награды.НайтиПоНаименованию("Грамота");
    
    // В этой строке возникает ошибка
    МенеджерЗаписи.Записать(); 
КонецПроцедуры

При выполнении этого кода мы получаем ошибку, в которой система сообщает, что значение поля НомерПоПорядку не соответствует отбору и указывает на существующую запись с номером 1. Более того, если в отладчике проверить значение МенеджерЗаписи.НомерПоПорядку прямо перед вызовом Записать(), оно будет равно 2, но в момент выполнения записи оно каким-то образом меняется на 1.

Причина такого поведения кроется в типовых механизмах контроля целостности данных, встроенных в конфигурацию ЗУП. В частности, за это отвечает подписка на событие ПроверитьЗаписиДанныхФизическихЛиц. Эта подписка срабатывает перед записью данных в регистры, связанные с физическими лицами, и выполняет дополнительные проверки. В нашем случае, она, вероятно, некорректно обрабатывает добавление новой записи и "сбрасывает" значение одного из ключевых полей, что и приводит к конфликту и ошибке.

Попытка использовать НаборЗаписей вместо МенеджераЗаписи приведет к той же ошибке, так как проблема не в объекте, который мы используем для записи, а в обработчике события, который срабатывает в любом случае.


// Этот код также вызовет ошибку
Сотрудник = Справочники.ФизическиеЛица.НайтиПоКоду("0005001");
НаборЗаписей = РегистрыСведений.НаградыФизическихЛиц.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ФизическоеЛицо.Установить(Сотрудник);

// Даже если мы явно установим отбор по новому номеру
НаборЗаписей.Отбор.НомерПоПорядку.Установить(2);

НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.ФизическоеЛицо = Сотрудник;
НоваяЗапись.НомерПоПорядку = 2;
НоваяЗапись.Награда = Справочники.Награды.НайтиПоНаименованию("Грамота");

НаборЗаписей.Записать(Истина); // Ошибка возникнет здесь

Способы решения проблемы

Существует несколько способов обойти эту проверку. Рассмотрим их от наименее к наиболее предпочтительному.

1. Отключение подписки на событие (не рекомендуется)

Самый прямолинейный, но и самый опасный способ — временно или навсегда отключить в конфигураторе подписку на событие ПроверитьЗаписиДанныхФизическихЛиц для объекта РегистрСведений.НаградыФизическихЛиц.

Почему это плохая идея?

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

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

2. Использование режима загрузки данных

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

Чтобы его использовать, нужно перед записью установить соответствующее свойство для набора записей.

Пример кода:


НаборЗаписей = РегистрыСведений.НаградыФизическихЛиц.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ФизическоеЛицо.Установить(Сотрудник);

// Включаем режим загрузки, чтобы обойти проверки
НаборЗаписей.ОбменДанными.Загрузка = Истина;

НоваяЗапись = НаборЗаписей.Добавить();
// ...заполнение полей новой записи...
НоваяЗапись.ФизическоеЛицо = Сотрудник;
НоваяЗапись.НомерПоПорядку = 2;
// ...

НаборЗаписей.Записать();

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

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

Наиболее правильным, современным и безопасным методом является использование механизма дополнительных свойств. Это специальная коллекция, доступная у многих объектов (документов, наборов записей), которая позволяет передать параметры из вашего кода внутрь обработчиков событий (в том числе в подписки).

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

Для нашей ситуации необходимо перед записью добавить в коллекцию ДополнительныеСвойства ключ с именем "НеПроверятьЗаписиДанныхФизическихЛиц".

Итоговый, правильный код:


&НаСервере
Процедура ЗагрузитьНаградыФизЛиц_НаСервере()
    Сотрудник = Справочники.ФизическиеЛица.НайтиПоКоду("0000004372");
    
    НаборЗаписей = РегистрыСведений.НаградыФизическихЛиц.СоздатьНаборЗаписей();
    // Важно установить отбор по одному из измерений, 
    // чтобы не затереть записи по другим физлицам
    НаборЗаписей.Отбор.ФизическоеЛицо.Установить(Сотрудник);
    
    // Читаем существующие записи, чтобы не удалить их, а дополнить
    НаборЗаписей.Прочитать();
    
    // Добавляем новую запись
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.ФизическоеЛицо = Сотрудник;
    НоваяЗапись.НомерПоПорядку = 2;
    НоваяЗапись.Награда = Справочники.Награды.НайтиПоНаименованию("Грамота");
    
    // *** Ключевой момент! ***
    // Сообщаем подписке на событие, что эту проверку нужно пропустить
    НаборЗаписей.ДополнительныеСвойства.Вставить("НеПроверятьЗаписиДанныхФизическихЛиц", Истина);
    
    // Записываем набор. Параметр Ложь (или не указывать) означает, 
    // что записи будут добавлены к существующим в рамках отбора.
    // Если указать Истина, существующие записи по отбору будут замещены.
    НаборЗаписей.Записать();
КонецПроцедуры

Этот метод является предпочтительным, потому что:

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

← На главную