Как исправить ошибку "Поле объекта не обнаружено" при проведении документа в 1С?

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

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

Анализируем исходную проблему и код

Представим ситуацию: мы пишем обработку проведения для документа "Потребление". Задача — списать продукты со склада, сделав расходные движения по регистру накопления "Продукты". Во время проведения документа система прерывает работу и выдает сообщение:

Ошибка при выполнении обработчика - 'ОбработкаПроведения' по причине: Поле объекта не обнаружено (Количество)

Система также указывает на строку кода, где произошла ошибка. Проанализируем сам код обработчика:


Процедура ОбработкаПроведения(Отказ, Режим)
    
    // регистр Продукты Расход
    Движения.Продукты.Записывать = Истина;
    
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
                  |    ПотреблениеПродукты.Продукт КАК Продукт,
                  |    СУММА(ПотреблениеПродукты.Количество) КАК Количесвто
                  |ИЗ
                  |    Документ.Потребление.Продукты КАК ПотреблениеПродукты
                  |ГДЕ
                  |    ПотреблениеПродукты.Ссылка = &Ссылка
                  |
                  |СГРУППИРОВАТЬ ПО
                  |    ПотреблениеПродукты.Продукт";
    
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    ВыборкаПоТоварамДокумента = РезультатЗапроса.Выбрать();
    
    Пока ВыборкаПоТоварамДокумента.Следующий() Цикл
                
        СебестоимостьПродукта = 0;
        
        Запрос = Новый Запрос;
        Запрос.Текст = "ВЫБРАТЬ
                      |    ПродуктыОстатки.Продукт КАК Продукт,
                      |    ПродуктыОстатки.КоличествоОстаток КАК КоличествоОстаток,
                      |    ПродуктыОстатки.СуммаОстаток КАК СуммаОстаток
                      |ИЗ
                      |    РегистрНакопления.Продукты.Остатки(&ДатаЗапроса, Продукт = &ОтборПоПродукту) КАК ПродуктыОстатки";
        
        Запрос.УстановитьПараметр("ДатаЗапроса", МоментВремени());
        Запрос.УстановитьПараметр("ОтборПоПродукту", ВыборкаПоТоварамДокумента.Продукт);
        
        РезультатЗапроса = Запрос.Выполнить();
        ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
        
        Если ВыборкаДетальныеЗаписи.Следующий() Тогда                            
            Если ВыборкаДетальныеЗаписи.КоличествоОстаток > 0 Тогда
                СебестоимостьПродукта = ВыборкаДетальныеЗаписи.СуммаОстаток / ВыборкаДетальныеЗаписи.КоличествоОстаток;
            КонецЕсли;
        КонецЕсли;
    
        Движение = Движения.Продукты.Добавить();
        Движение.ВидДвижения    = ВидДвиженияНакопления.Расход;
        Движение.Период         = Дата;
        Движение.Продукт        = ВыборкаПоТоварамДокумента.Продукт;
        // Вот здесь возникает ошибка
        Движение.Количество     = ВыборкаПоТоварамДокумента.Количество;
        Движение.Сумма          = ВыборкаПоТоварамДокумента.Количество * СебестоимостьПродукта;
        
     КонецЦикла;
 КонецПроцедуры

Ошибка возникает при попытке обратиться к полю ВыборкаПоТоварамДокумента.Количество. Система сообщает, что такого поля в выборке нет. Давайте внимательно посмотрим на текст первого запроса, который формирует эту выборку.

В секции ВЫБРАТЬ мы видим следующую строку:

СУММА(ПотреблениеПродукты.Количество) КАК Количесвто

Причина ошибки кроется в банальной опечатке. Вместо правильного псевдонима "Количество" написано "Количесвто". В результате выполнения запроса создается колонка с именем "Количесвто", а код пытается обратиться к колонке "Количество", которой, естественно, не существует. Это и вызывает ошибку.

Решение 1: Исправляем орфографическую ошибку

Самое простое и прямое решение — исправить опечатку в тексте запроса. Это немедленно решит проблему с проведением документа.

Неправильный вариант:

СУММА(ПотреблениеПродукты.Количество) КАК Количесвто

Правильный вариант:

СУММА(ПотреблениеПродукты.Количество) КАК Количество

После этого исправления код будет работать корректно, так как имя поля в выборке будет совпадать с именем, к которому обращается программный код.

Решение 2: Оптимизация кода и избавление от запроса в цикле

Хотя основная ошибка исправлена, текущий код имеет серьезный недостаток с точки зрения производительности — запрос в цикле. На каждой итерации цикла по продуктам из документа выполняется отдельный запрос к базе данных для получения остатков. Если в документе будет 100 позиций, то будет выполнен 101 запрос (один основной и сто в цикле). Это создает избыточную нагрузку на систему и может серьезно замедлить проведение документов.

Правильный подход — получить все необходимые данные за одно обращение к базе данных. Для этого объединим два запроса в один с помощью ЛЕВОГО СОЕДИНЕНИЯ.

Рассмотрим, как должен выглядеть оптимизированный запрос:


ВЫБРАТЬ
    ПотреблениеПродукты.Продукт КАК Продукт,
    СУММА(ПотреблениеПродукты.Количество) КАК Количество,
    ЕСТЬNULL(ПродуктыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
    ЕСТЬNULL(ПродуктыОстатки.СуммаОстаток, 0) КАК СуммаОстаток
ИЗ
    Документ.Потребление.Продукты КАК ПотреблениеПродукты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продукты.Остатки(
                &МоментВремени,
                Продукт В (&СписокПродуктов)) КАК ПродуктыОстатки
        ПО ПотреблениеПродукты.Продукт = ПродуктыОстатки.Продукт
ГДЕ
    ПотреблениеПродукты.Ссылка = &Ссылка
СГРУППИРОВАТЬ ПО
    ПотреблениеПродукты.Продукт,
    ПродуктыОстатки.КоличествоОстаток,
    ПродуктыОстатки.СуммаОстаток

Что мы здесь сделали:

  1. Объединили табличную часть документа Документ.Потребление.Продукты с виртуальной таблицей остатков РегистрНакопления.Продукты.Остатки.
  2. Использовали ЛЕВОЕ СОЕДИНЕНИЕ, чтобы в результат попали все продукты из документа, даже если по ним нет остатков в регистре.
  3. Для обработки случаев, когда остатков нет, применили функцию ЕСТЬNULL. Она заменяет значение NULL (которое вернется для полей из правой таблицы при отсутствии соединения) на 0.
  4. В параметры виртуальной таблицы остатков передали список продуктов из документа (Продукт В (&СписокПродуктов)). Это важно для производительности, чтобы регистр рассчитывал остатки только по нужным позициям.

Теперь перепишем всю процедуру проведения с использованием нового подхода.


Процедура ОбработкаПроведения(Отказ, Режим)
    
    Движения.Продукты.Записывать = Истина;

    // Сначала соберем список продуктов для передачи в параметр запроса
    СписокПродуктов = ЭтотОбъект.Продукты.ВыгрузитьКолонку("Продукт");

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ПотреблениеПродукты.Продукт КАК Продукт,
        |    СУММА(ПотреблениеПродукты.Количество) КАК Количество,
        |    ЕСТЬNULL(ПродуктыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
        |    ЕСТЬNULL(ПродуктыОстатки.СуммаОстаток, 0) КАК СуммаОстаток
        |ИЗ
        |    Документ.Потребление.Продукты КАК ПотреблениеПродукты
        |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продукты.Остатки(
        |                &МоментВремени,
        |                Продукт В (&СписокПродуктов)) КАК ПродуктыОстатки
        |        ПО ПотреблениеПродукты.Продукт = ПродуктыОстатки.Продукт
        |ГДЕ
        |    ПотреблениеПродукты.Ссылка = &Ссылка
        |СГРУППИРОВАТЬ ПО
        |    ПотреблениеПродукты.Продукт,
        |    ПродуктыОстатки.КоличествоОстаток,
        |    ПродуктыОстатки.СуммаОстаток";

    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
    Запрос.УстановитьПараметр("СписокПродуктов", СписокПродуктов);

    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();

    Пока Выборка.Следующий() Цикл
        
        СебестоимостьПродукта = 0;
        Если Выборка.КоличествоОстаток > 0 Тогда
            СебестоимостьПродукта = Выборка.СуммаОстаток / Выборка.КоличествоОстаток;
        КонецЕсли;

        Движение = Движения.Продукты.Добавить();
        Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
        Движение.Период      = Дата;
        Движение.Продукт     = Выборка.Продукт;
        Движение.Количество  = Выборка.Количество;
        Движение.Сумма       = Выборка.Количество * СебестоимостьПродукта;
        
    КонецЦикла;

КонецПроцедуры

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

Как избежать подобных ошибок в будущем?

Чтобы минимизировать риск опечаток и других синтаксических ошибок в коде, используйте встроенные инструменты платформы 1С:

Таким образом, мы не только исправили критическую ошибку, но и улучшили код, сделав его более эффективным и соответствующим лучшим практикам разработки на 1С.

← На главную