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