Обращение к процедуре объекта как к функции при использовании метода Свернуть в 1С

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

В процессе разработки во встроенном языке 1С:Предприятие программисты часто сталкиваются с ошибкой «Обращение к процедуре объекта как к функции». Эта проблема особенно популярна при попытке написать лаконичный код «в одну строку» для свертки и загрузки табличных частей или таблиц значений. В этой статье мы подробно разберем, почему возникает данная ошибка, проанализируем сопутствующие логические изъяны в алгоритмах обработки таблиц и рассмотрим наиболее эффективные и безопасные способы решения этой задачи.

Выясняем причину синтаксической ошибки

Давайте посмотрим на исходную строку кода, которая вызвала сбой на стороне пользователя:


Объект.Услуга.Загрузить(Объект.Услуга.Выгрузить().Свернуть("Наименование, Цена", "Количество, Сумма"));

При попытке выполнить эту конструкцию платформа выдает ошибку. Давайте разберем по шагам, что здесь происходит:

  1. Метод Объект.Услуга.Выгрузить() успешно отрабатывает и возвращает объект типа ТаблицаЗначений, содержащий копию данных табличной части.
  2. Далее к этой временной таблице применяется метод Свернуть("Наименование, Цена", "Количество, Сумма").
  3. Метод Свернуть() во встроенном языке 1С является процедурой, а не функцией. Он преобразует исходную таблицу значений «на месте» (in-place) и не возвращает никакого значения (возвращает значение Неопределено).
  4. Поскольку метод не возвращает таблицу значений, внешняя функция Загрузить() пытается обработать значение Неопределено вместо ожидаемого объекта типа ТаблицаЗначений, что и приводит к ошибке выполнения.

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


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

Критический разбор логики исходного алгоритма

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

Проанализируем фрагмент кода из первого сообщения темы:


Если Объект.Услуга.Количество() > 0 Тогда
    Для каждого стр из Объект.Услуга Цикл
        Если стр.Наименование = ВыбСтрока.Услуга Тогда
            Стр.Количество = Стр.Количество + 1;
            Стр.Сумма = Стр.Цена * Стр.Количество;
        Иначе
            НСТР = Объект.Услуга.Добавить(); 
            НСТР.Наименование = ВыбСтрока.Услуга;
            ...
            Объект.Услуга.Загрузить(Объект.Услуга.Выгрузить().Свернуть(...));
        КонецЕсли;
    КонецЦикла;
...

Ошибка №1: Изменение коллекции внутри цикла обхода

В коде используется цикл Для каждого стр из Объект.Услуга Цикл. Внутри этого цикла (в ветке Иначе) происходит добавление новой строки через метод Добавить() и перезапись всей коллекции через Загрузить(). В 1С категорически запрещено модифицировать состав коллекции во время ее обхода. Это приводит к непредсказуемому сдвигу индексов итератора: цикл может пропустить строки, пойти по бесконечному кругу или аварийно завершиться с системным исключением.

Ошибка №2: Ошибочная логика ветвления внутри цикла

Конструкция проверки устроена так, что если текущая строка обхода не совпадает с искомой услугой, управление мгновенно переходит в ветку Иначе, где добавляется новая строка. Например, если в табличной части уже есть три разные услуги (А, Б и В), и мы пытаемся добавить услугу «Б»:

В результате этот алгоритм будет добавлять новую позицию на каждом шаге несовпадения, увеличивая количество записей в геометрической прогрессии.

Скрытая опасность метода Свернуть для табличных частей

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

Если ваша табличная часть содержит другие важные реквизиты (например, СтавкаНДС, ЕдиницаИзмерения, Спецификация или даже служебный реквизит НомерСтроки), то после выполнения Свернуть() и последующего Загрузить() эти колонки будут полностью очищены и примут значения по умолчанию. Это может привести к потере важных данных в документе.

Рекомендуемые варианты решения задачи

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

Способ 1: Использование метода НайтиСтроки (Рекомендуемый)

Это самый оптимальный и лаконичный вариант. Вместо перебора всей таблицы в цикле мы выполняем точечный поиск строки по ключевым реквизитам. Если строка найдена — увеличиваем показатели, если нет — создаем новую.


// Шаг 1. Задаем параметры поиска по ключевым полям
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Наименование", ВыбСтрока.Услуга);
ПараметрыОтбора.Вставить("Цена",         ВыбСтрока.Цена);

// Шаг 2. Ищем существующие строки в табличной части
НайденныеСтроки = Объект.Услуга.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() > 0 Тогда
    // Шаг 3. Если нашли — берем первую строку (на случай дублей) и обновляем показатели
    ТекущаяСтрока = НайденныеСтроки[0];
    ТекущаяСтрока.Количество = ТекущаяСтрока.Количество + 1;
    ТекущаяСтрока.Сумма      = ТекущаяСтрока.Цена * ТекущаяСтрока.Количество;
Иначе
    // Шаг 4. Если совпадений нет — добавляем новую строку в табличную часть
    НоваяСтрока = Объект.Услуга.Добавить();
    НоваяСтрока.Наименование = ВыбСтрока.Услуга;
    НоваяСтрока.Цена         = ВыбСтрока.Цена;
    НоваяСтрока.Количество   = 1;
    НоваяСтрока.Сумма        = НоваяСтрока.Цена * НоваяСтрока.Количество;
КонецЕсли;

Способ 2: Накопление строк с разовой сверткой в конце процедуры

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

Рассмотрим пример реализации этого подхода:


// 1. Быстро и безусловно добавляем все новые строки из внешнего источника в цикле
Для каждого ВыбСтрока Из МассивНовыхСтрок Цикл
    НоваяСтрока = Объект.Услуга.Добавить();
    НоваяСтрока.Наименование = ВыбСтрока.Услуга;
    НоваяСтрока.Цена         = ВыбСтрока.Цена;
    НоваяСтрока.Количество   = ВыбСтрока.Количество;
    НоваяСтрока.Сумма        = НоваяСтрока.Цена * НоваяСтрока.Количество;
КонецЦикла;

// 2. Выполняем свертку ТЧ ОДИН раз за пределами цикла
ТаблицаДанных = Объект.Услуга.Выгрузить();

// ВАЖНО: В группировку включаем абсолютно все реквизиты, данные которых нужно сохранить!
КолонкиГруппировки  = "Наименование, Цена, СтавкаНДС, ЕдиницаИзмерения";
КолонкиСуммирования = "Количество, Сумма";

ТаблицаДанных.Свернуть(КолонкиГруппировки, КолонкиСуммирования);

// 3. Возвращаем свернутые данные обратно в табличную часть документа
Объект.Услуга.Загрузить(ТаблицаДанных);

Применение любого из предложенных вариантов не только навсегда избавит вас от ошибки «Обращение к процедуре как к функции», но и значительно повысит скорость работы вашей программы, защитив данные от случайной потери или перезаписи.

← На главную