При разработке обработок по переносу данных или заполнению дополнительных реквизитов программисты часто сталкиваются с ситуацией, когда платформа блокирует запись в регистр сведений ЗначенияСвойствОбъектов. Типичный сценарий: данные берутся из справочника Номенклатура и должны попасть в регистр для использования в характеристиках. Однако вместо успешного выполнения кода возникает ошибка "Запись не верна! Значение поля 'Свойство' не может быть пустым". В этой статье мы подробно разберем, почему это происходит и как правильно организовать цикл обработки данных.
Рассмотрим ситуацию, представленную в коде автора. Основная задача — найти конкретное свойство в Плане видов характеристик (ПВХ) по коду и записать значение объема из единицы измерения номенклатуры в регистр сведений. Проблема заключается в том, что даже при наличии условия "Если", код продолжает выполняться некорректно или прерывается с ошибкой компиляции.
Проанализируем фрагмент кода, который вызывает затруднения:
Функция ЗаполнениеСвойстваОбъемМ3()
ОбъемМ3 = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00158");
// ... запрос и выборка ...
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Свойство = ОбъемМ3;
// ... заполнение остальных полей ...
МенеджерЗаписи.Записать();
Если ОбъемМ3.Пустая() Тогда
ВыборкаДетальныеЗаписи.Следующий(); // Ошибка: это пропускает запись в выборке, но не прерывает итерацию цикла
КонецЕсли;
КонецЦикла;
КонецФункции
В данном примере мы видим сразу несколько критических ошибок. Во-первых, проверка на пустое свойство идет после попытки записи. Во-вторых, использование Выборка.Следующий() внутри условия просто сдвигает курсор выборки, но текущая итерация цикла Пока продолжается, что приводит к попытке записи пустых данных.
Метод НайтиПоКоду() в 1С всегда возвращает ссылку. Если элемент с таким кодом не найден, возвращается Пустая ссылка соответствующего типа, а не Неопределено. Разберем, почему проверка должна быть первичной:
ОбъемМ3 содержит пустую ссылку.Свойство в регистре ЗначенияСвойствОбъектов является ведущим и обязательным (оно входит в ключ записи).Для решения этой проблемы мы должны добавить проверку сразу после поиска свойства. Также важно учитывать требования синтаксиса: если мы находимся внутри функции, оператор Возврат должен возвращать какое-либо значение, иначе в обычном приложении (толстом клиенте) возникнет ошибка "Ожидается выражение".
Рассмотрим правильный вариант начала функции:
ОбъемМ3 = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00158");
Если ОбъемМ3.Пустая() Тогда
Сообщить("Свойство с кодом 00158 не найдено в базе данных!");
Возврат Ложь; // Или Возврат Неопределено;
КонецЕсли;
Выясним одну из самых коварных причин ошибки, которая часто ускользает от внимания. Это затенение переменной реквизитом формы. Если вы создаете обработку и добавили на форму реквизит с именем ОбъемМ3 (например, числового типа для ввода параметров), а в коде модуля используете переменную с тем же именем, 1С может обратиться к реквизиту формы вместо вашей локальной переменной.
Посмотрим, к чему это приводит:
ОбъемМ3 = ПланыВидовХарактеристик...НайтиПоКоду(...).ОбъемМ3 типа "Число", система попытается привести ссылку к числу или возникнет путаница в контекстах.МенеджерЗаписи.Свойство передается либо 0, либо пустая ссылка из контекста формы, а не найденный элемент ПВХ.Рекомендация: Всегда используйте уникальные имена для локальных переменных в коде, например, добавляя префикс л_ или Текущее. Например: л_СвойствоСсылка.
Разберем ситуацию, когда само свойство найдено, но данные в запросе (например, объем номенклатуры) могут быть не заполнены. Если мы попытаемся записать "пустое значение" в ресурс регистра, ошибка может не возникнуть (зависит от настроек регистра), но если пусто само измерение Объект (Номенклатура), ошибка неизбежна.
Проанализируем правильную структуру цикла с использованием оператора Продолжить:
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Проверяем, заполнена ли номенклатура и объем в текущей строке
Если НЕ ЗначениеЗаполнено(ВыборкаДетальныеЗаписи.Номенклатура) Тогда
Продолжить; // Переходим к следующей итерации, не выполняя код ниже
КонецЕсли;
МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = ВыборкаДетальныеЗаписи.Номенклатура;
МенеджерЗаписи.Свойство = ОбъемМ3;
// Читаем существующую запись, чтобы не перезаписывать лишний раз или обновить текущую
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.Объект = ВыборкаДетальныеЗаписи.Номенклатура;
МенеджерЗаписи.Свойство = ОбъемМ3;
МенеджерЗаписи.Значение = ВыборкаДетальныеЗаписи.Объем;
МенеджерЗаписи.Записать();
КонецЦикла;
Использование МенеджерЗаписи внутри цикла — это крайне ресурсоемкая операция, так как на каждом шаге происходит отдельное обращение к базе данных. Проанализируем более профессиональный подход с использованием объекта РегистрСведенийНаборЗаписей.
Рассмотрим пример кода, который работает быстрее и надежнее:
// Создаем набор записей один раз вне цикла
НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Проверяем корректность данных
Если НЕ ЗначениеЗаполнено(ВыборкаДетальныеЗаписи.Объем) Тогда
Продолжить;
КонецЕсли;
// Очищаем фильтр набора для нового объекта
НаборЗаписей.Отбор.Объект.Установить(ВыборкаДетальныеЗаписи.Номенклатура);
НаборЗаписей.Отбор.Свойство.Установить(ОбъемМ3);
// Создаем новую строку в наборе
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Объект = ВыборкаДетальныеЗаписи.Номенклатура;
НоваяЗапись.Свойство = ОбъемМ3;
НоваяЗапись.Значение = ВыборкаДетальныеЗаписи.Объем;
// Записываем набор (перезаписывает данные по данному Объекту и Свойству)
Попытка
НаборЗаписей.Записать();
Исключение
Сообщить("Ошибка записи для: " + ВыборкаДетальныеЗаписи.Номенклатура + " - " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Чтобы окончательно избавиться от ошибки "Значение поля свойства не может быть пустым", выполним следующие шаги:
НайтиПоКоду или НайтиПоНаименованию действительно находит элемент. Используем ЗначениеЗаполнено() или .Пустая() перед входом в цикл.ГДЕ Номенклатура.ЕдиницаХраненияОстатков.Объем > 0. Это позволит нам не обрабатывать заведомо пустые или некорректные записи еще на уровне базы данных.Продолжить, чтобы пропустить текущий шаг цикла.Записать() в блок Попытка...Исключение. Это не исправит логическую ошибку, но позволит программе не "вылетать" полностью, а вывести информативное сообщение о том, на каком именно объекте произошел сбой.Следуя этим правилам, мы обеспечим стабильную работу обработки и исключим появление системных ошибок при работе с регистрами сведений.