В практике разработки на платформе 1С:Предприятие 8.3 часто возникает необходимость переиспользовать сложную бизнес-логику, уже написанную в общих модулях, для построения отчетов в Системе компоновки данных (СКД). Типичная ситуация: в конфигурации уже есть функция, которая рассчитывает сроки поставки, остатки или резервы, и мы хотим вывести эти данные в вычисляемом поле отчета. Однако при попытке вызвать такую функцию «в лоб» программисты часто сталкиваются с ошибками типизации и производительности. Рассмотрим подробнее, как правильно организовать передачу параметров и получение результата.
Проанализируем ситуацию, описанную автором темы. Имеется функция ОбновитьСрокиПоставки, которая выполняет сложные расчеты внутри цикла по товарам документа, заполняет таблицу мТовары и в конце помещает некое значение во временное хранилище, возвращая адрес. Ошибка «Значение не является значением объектного типа (НайтиСтроки)» возникает в функции-обертке по двум причинам:
СтрокаТовара.Фирма_Поставка, которая является обычной строкой (например, «Все в наличии»). В функции-обертке программист пытается применить метод НайтиСтроки() к результату, полученному из хранилища. Поскольку у типа Строка нет метода НайтиСтроки (он есть только у ТаблицыЗначений), система выдает ошибку.ПоместитьВоВременноеХранилище происходит вне цикла, но обращается к переменной СтрокаТовара, которая была итератором цикла. Это означает, что в хранилище попадет результат только для самой последней строки документа, а не для всей таблицы.Если мы не можем полностью переписать исходную функцию (например, она используется в логике проведения или заполнения формы документа), нам необходимо изменить то, что она возвращает. Чтобы иметь возможность найти данные по конкретной строке (по КодСтроки), нам нужно помещать в хранилище всю таблицу целиком.
Рассмотрим, как должна выглядеть измененная часть первой функции:
// В конце функции ОбновитьСрокиПоставки
// Вместо возврата одной строки возвращаем всю таблицу товаров
Возврат ПоместитьВоВременноеХранилище(мТовары);
КонецФункции
Теперь проанализируем, как должна выглядеть функция-обертка ОпределитьСрокПоставки, которую мы вызываем из вычисляемого поля СКД. Нам нужно извлечь таблицу из хранилища и найти в ней нужную строку по идентификатору:
Функция ОпределитьСрокПоставки(ДокументЗаказ, КодСтроки) Экспорт
// Получаем адрес хранилища, вызвав основную функцию
АдресХранилища = ОбновитьСрокиПоставки(ДокументЗаказ);
// Извлекаем таблицу значений
ТаблицаСрокПоставки = ПолучитьИзВременногоХранилища(АдресХранилища);
Если ТипЗнч(ТаблицаСрокПоставки) = Тип("ТаблицаЗначений") Тогда
Отбор = Новый Структура("КодСтроки", КодСтроки);
НайденныеСтроки = ТаблицаСрокПоставки.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
Возврат НайденныеСтроки[0].Фирма_Поставка;
КонецЕсли;
КонецЕсли;
Возврат "";
КонецФункции
Выясним причину, по которой отчет может начать «тормозить» или выдавать ошибки при увеличении периода. Вызов функции общего модуля из вычисляемого поля происходит для каждой строки отчета. Если в отчете 1000 строк товаров, то функция ОбновитьСрокиПоставки будет вызвана 1000 раз. Внутри этой функции выполняются запросы к базе данных (запрос в цикле) и работа с временным хранилищем. Это крайне неоптимально.
Для оптимизации рекомендуется использовать кэширование или модуль с повторным использованием значений. Однако самым правильным подходом будет отказ от вычисляемого поля в пользу Внешнего набора данных.
Посмотрим, как реализовать это по шагам:
ЗаказКлиента, которые попали в результат.ТаблицуЗначений и передайте её в СКД как «Набор данных - объект».При вызове функций из СКД часто возникает ошибка «Итератор для значения не определен». Проанализируем ситуацию: в коде функции используется обращение ДокументЗаказ.Товары.Выгрузить(). Если в параметре ДокументЗаказ передана Ссылка, то при обращении к табличной части через точку 1С автоматически пытается прочитать объект из базы. Однако в некоторых контекстах (например, в фоновых заданиях или при специфических правах доступа) это может работать некорректно.
Если вы работаете в СКД, лучше передавать ссылку и внутри функции явно получать объект, если это необходимо для модификации, или использовать запросы для получения данных табличной части:
// Пример безопасного получения таблицы товаров из ссылки
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ИЗ Документ.ЗаказКлиента.Товары ГДЕ Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ДокументЗаказ);
мТовары = Запрос.Выполнить().Выгрузить();
Подведем итог: чтобы вытянуть параметр из сложной функции для СКД, мы должны обеспечить передачу объекта ТаблицаЗначений через временное хранилище или напрямую. Основная ошибка автора заключалась в попытке вызвать метод НайтиСтроки() у строкового значения.
Помните: работа с ПоместитьВоВременноеХранилище внутри отчетов требует осторожности, так как адреса хранилища живут в течение сеанса и могут переполнять память сервера при массовых вызовах. Всегда проверяйте типы возвращаемых значений с помощью ТипЗнч() и старайтесь избегать тяжелых запросов внутри функций, вызываемых из полей компоновки.