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

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

При разработке обработок по переносу данных или заполнению дополнительных реквизитов программисты часто сталкиваются с ситуацией, когда платформа блокирует запись в регистр сведений ЗначенияСвойствОбъектов. Типичный сценарий: данные берутся из справочника Номенклатура и должны попасть в регистр для использования в характеристиках. Однако вместо успешного выполнения кода возникает ошибка "Запись не верна! Значение поля 'Свойство' не может быть пустым". В этой статье мы подробно разберем, почему это происходит и как правильно организовать цикл обработки данных.

Анализ исходной проблемы

Рассмотрим ситуацию, представленную в коде автора. Основная задача — найти конкретное свойство в Плане видов характеристик (ПВХ) по коду и записать значение объема из единицы измерения номенклатуры в регистр сведений. Проблема заключается в том, что даже при наличии условия "Если", код продолжает выполняться некорректно или прерывается с ошибкой компиляции.

Проанализируем фрагмент кода, который вызывает затруднения:


Функция ЗаполнениеСвойстваОбъемМ3()
    ОбъемМ3 = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00158");
    // ... запрос и выборка ...
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Свойство = ОбъемМ3;
        // ... заполнение остальных полей ...
        МенеджерЗаписи.Записать();
        
        Если ОбъемМ3.Пустая() Тогда
             ВыборкаДетальныеЗаписи.Следующий(); // Ошибка: это пропускает запись в выборке, но не прерывает итерацию цикла
        КонецЕсли;     
    КонецЦикла;
КонецФункции

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

Причина №1: Неверное использование метода НайтиПоКоду

Метод НайтиПоКоду() в 1С всегда возвращает ссылку. Если элемент с таким кодом не найден, возвращается Пустая ссылка соответствующего типа, а не Неопределено. Разберем, почему проверка должна быть первичной:

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

Для решения этой проблемы мы должны добавить проверку сразу после поиска свойства. Также важно учитывать требования синтаксиса: если мы находимся внутри функции, оператор Возврат должен возвращать какое-либо значение, иначе в обычном приложении (толстом клиенте) возникнет ошибка "Ожидается выражение".

Рассмотрим правильный вариант начала функции:


ОбъемМ3 = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00158");
Если ОбъемМ3.Пустая() Тогда
    Сообщить("Свойство с кодом 00158 не найдено в базе данных!");
    Возврат Ложь; // Или Возврат Неопределено;
КонецЕсли;

Причина №2: Конфликт имен и затенение переменных

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

Посмотрим, к чему это приводит:

  1. Вы присваиваете ОбъемМ3 = ПланыВидовХарактеристик...НайтиПоКоду(...).
  2. Если на форме есть реквизит ОбъемМ3 типа "Число", система попытается привести ссылку к числу или возникнет путаница в контекстах.
  3. В итоге, при записи в МенеджерЗаписи.Свойство передается либо 0, либо пустая ссылка из контекста формы, а не найденный элемент ПВХ.

Рекомендация: Всегда используйте уникальные имена для локальных переменных в коде, например, добавляя префикс л_ или Текущее. Например: л_СвойствоСсылка.

Причина №3: Пропуск пустых значений в цикле

Разберем ситуацию, когда само свойство найдено, но данные в запросе (например, объем номенклатуры) могут быть не заполнены. Если мы попытаемся записать "пустое значение" в ресурс регистра, ошибка может не возникнуть (зависит от настроек регистра), но если пусто само измерение Объект (Номенклатура), ошибка неизбежна.

Проанализируем правильную структуру цикла с использованием оператора Продолжить:


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

    МенеджерЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Объект   = ВыборкаДетальныеЗаписи.Номенклатура;
    МенеджерЗаписи.Свойство = ОбъемМ3;
    
    // Читаем существующую запись, чтобы не перезаписывать лишний раз или обновить текущую
    МенеджерЗаписи.Прочитать();
    
    МенеджерЗаписи.Объект   = ВыборкаДетальныеЗаписи.Номенклатура;
    МенеджерЗаписи.Свойство = ОбъемМ3;
    МенеджерЗаписи.Значение = ВыборкаДетальныеЗаписи.Объем;
    
    МенеджерЗаписи.Записать();
КонецЦикла;

Оптимизация записи (Набор записей)

Использование МенеджерЗаписи внутри цикла — это крайне ресурсоемкая операция, так как на каждом шаге происходит отдельное обращение к базе данных. Проанализируем более профессиональный подход с использованием объекта РегистрСведенийНаборЗаписей.

Рассмотрим пример кода, который работает быстрее и надежнее:


// Создаем набор записей один раз вне цикла
НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    // Проверяем корректность данных
    Если НЕ ЗначениеЗаполнено(ВыборкаДетальныеЗаписи.Объем) Тогда
        Продолжить;
    КонецЕсли;
    
    // Очищаем фильтр набора для нового объекта
    НаборЗаписей.Отбор.Объект.Установить(ВыборкаДетальныеЗаписи.Номенклатура);
    НаборЗаписей.Отбор.Свойство.Установить(ОбъемМ3);
    
    // Создаем новую строку в наборе
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Объект   = ВыборкаДетальныеЗаписи.Номенклатура;
    НоваяЗапись.Свойство = ОбъемМ3;
    НоваяЗапись.Значение = ВыборкаДетальныеЗаписи.Объем;
    
    // Записываем набор (перезаписывает данные по данному Объекту и Свойству)
    Попытка
        НаборЗаписей.Записать();
    Исключение
        Сообщить("Ошибка записи для: " + ВыборкаДетальныеЗаписи.Номенклатура + " - " + ОписаниеОшибки());
    КонецПопытки;
КонецЦикла;

Итоговый чек-лист для исправления ошибки

Чтобы окончательно избавиться от ошибки "Значение поля свойства не может быть пустым", выполним следующие шаги:

  1. Проверка поиска свойства: Убедимся, что НайтиПоКоду или НайтиПоНаименованию действительно находит элемент. Используем ЗначениеЗаполнено() или .Пустая() перед входом в цикл.
  2. Контроль типов: Убедимся, что переменная, в которой хранится свойство, не "перебивается" реквизитами формы. Лучше переименовать локальную переменную в нечто уникальное.
  3. Валидация данных в запросе: В тексте запроса добавим условие ГДЕ Номенклатура.ЕдиницаХраненияОстатков.Объем > 0. Это позволит нам не обрабатывать заведомо пустые или некорректные записи еще на уровне базы данных.
  4. Использование оператора Продолжить: Если в процессе цикла выясняется, что данные для записи некорректны, используем Продолжить, чтобы пропустить текущий шаг цикла.
  5. Обработка исключений: Обернем метод Записать() в блок Попытка...Исключение. Это не исправит логическую ошибку, но позволит программе не "вылетать" полностью, а вывести информативное сообщение о том, на каком именно объекте произошел сбой.

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

← На главную