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