При доработке типовых или нетиповых конфигураций на платформе 1С:Предприятие разработчики часто сталкиваются с необходимостью агрегации данных в коллекциях. Одним из наиболее популярных инструментов для этого является метод Свернуть() объекта ТаблицаЗначений. Однако при добавлении новых реквизитов в этот алгоритм легко совершить досадную синтаксическую или логическую ошибку, которая приводит к аварийному завершению программы с сообщением «Слишком много фактических параметров».
В этой статье мы подробно разберем, почему возникает данная ошибка, как правильно использовать синтаксис метода, а также исследуем скрытые логические ловушки группировки, которые могут привести к безвозвратной потере данных или искажению математических расчетов.
Платформа 1С генерирует данное исключение в тех случаях, когда в вызываемую функцию или метод передается большее количество аргументов, чем предусмотрено его объявлением (сигнатурой) в платформе или программном коде.
Давайте обратимся к синтакс-помощнику и проанализируем сигнатуру метода Свернуть() для объекта ТаблицаЗначений:
ТаблицаЗначений.Свернуть(<КолонкиГруппировок>, <КолонкиСуммирования>)
Как мы видим, данный метод принимает строго не более двух параметров:
Теперь посмотрим на фрагмент кода, который вызвал сбой у автора темы:
Товары.свернуть("Номенклатура, Заказ, СерияНоменклатуры", "Количество", "Коэффициент");
В данном примере разработчик передал в метод три отдельных аргумента, разделенных запятыми вне кавычек:
"Номенклатура, Заказ, СерияНоменклатуры" — воспринят платформой как первый параметр (колонки группировки)."Количество" — воспринят как второй параметр (колонки суммирования)."Коэффициент" — передан как третий параметр.Поскольку третьего параметра у метода Свернуть() не существует в природе, компилятор (или интерпретатор в режиме выполнения) незамедлительно сообщает об ошибке «Слишком много фактических параметров».
Если ваша задача состоит в том, чтобы просто сложить значения в колонках Количество и Коэффициент, синтаксически правильный вызов должен объединить их в один общий второй параметр. Имена колонок внутри параметров должны разделяться запятыми внутри строковых литералов:
// Правильное объединение суммируемых колонок в один строковый параметр
Товары.Свернуть("Номенклатура, Заказ, СерияНоменклатуры", "Количество, Коэффициент");
В этом случае платформа видит ровно два параметра и успешно выполняет операцию свертки.
Хотя первый вариант решает проблему синтаксиса, с точки зрения бизнес-логики и математики он содержит грубейшую ошибку. Давайте проанализируем ситуацию подробнее.
Колонка Коэффициент (например, коэффициент пересчета упаковки в базовые единицы хранения) — это неизменяемая характеристика номенклатурной позиции или единицы измерения. Суммировать коэффициенты разных строк физически бессмысленно.
Представим, что у нас есть две строки в таблице:
Если мы свернем таблицу, суммируя и количество, и коэффициент, то на выходе мы получим:
При последующем пересчете по формуле автора ТекСтрока.Количество * ТекСтрока.Коэффициент мы получим абсолютно неверный результат: 15 * 3.0 = 45 вместо правильных (5 * 1.5) + (10 * 1.5) = 22.5.
Правило: Коэффициенты, цены, ставки НДС и другие подобные показатели должны выступать в качестве критериев группировки, а не суммирования. Правильный вызов с точки зрения логики выглядит следующим образом:
// Переносим Коэффициент в первый параметр — колонку группировки
Товары.Свернуть("Номенклатура, Заказ, СерияНоменклатуры, Коэффициент", "Количество");
В таком сценарии строки будут объединены только при условии совпадения и номенклатуры, и коэффициента, а сам коэффициент сохранит свое исходное корректное значение.
Разберем еще один важнейший нюанс, который часто упускают начинающие программисты 1С. Метод Свернуть() работает непосредственно с исходным объектом ТаблицаЗначений (модифицирует коллекцию "на месте"). При этом происходит необратимый процесс: все колонки, которые не были перечислены ни в первом, ни во втором параметрах метода, полностью удаляются из таблицы значений.
В исходном коде автора после свертки идет обращение к процедуре:
ПроверитьТоварыПоЦепочкиСогласование(
ТекСтрока.Номенклатура,
ТекСтрока.Заказ,
ТекСтрока.СерийныйНомер,
ТекСтрока.СрокГодности,
ТекСтрока.Количество * ТекСтрока.Коэффициент,
НайденныеСтроки,
ЛогОшибок,
Ссылка._Номер
);
Обратите внимание: в параметры процедуры передаются свойства ТекСтрока.СерийныйНомер и ТекСтрока.СрокГодности. Но если перед этим мы выполнили свертку по колонкам "Номенклатура, Заказ, СерияНоменклатуры, Коэффициент", то колонок СерийныйНомер и СрокГодности в таблице Товары больше не существует!
Это неизбежно приведет к ошибке выполнения: «Поле объекта не обнаружено (СерийныйНомер)».
Чтобы избежать этой критической ошибки, все реквизиты, которые потребуются для дальнейшего анализа или передачи в другие процедуры, необходимо обязательно включить в первый параметр метода Свернуть() (колонки группировки):
// Включаем все необходимые для дальнейшей работы колонки в группировку
Товары.Свернуть("Номенклатура, Заказ, СерияНоменклатуры, СерийныйНомер, СрокГодности, Коэффициент", "Количество");
Для эффективного программирования в 1С важно понимать внутреннюю логику работы этого метода платформы:
Число и Строка), то при суммировании значения нечисловых типов будут проигнорированы платформой. Если колонка вообще не содержит числового типа данных, результатом суммирования станет 0.
// Оставляем только уникальные пары Номенклатура-Заказ
Товары.Свернуть("Номенклатура, Заказ");
Метод Свернуть() выполняется в оперативной памяти сервера (или клиента в толстом клиенте) и на таблицах большого объема (от десятков тысяч строк) может вызывать серьезные просадки производительности. Если перед вами стоит задача обработки крупного массива данных, гораздо более профессиональным решением будет использование механизма временных таблиц в запросах.
Рассмотрим пример, как можно выполнить ту же самую группировку более эффективно с использованием СУБД:
// Создаем менеджер временных таблиц для передачи исходных данных в запрос
МВТ = Новый МенеджерВременных Таблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МВТ;
// Передаем нашу таблицу значений в качестве параметра запроса
Запрос.УстановитьПараметр("ТаблицаТоваров", Товары);
// Выполняем группировку средствами СУБД
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Заказ КАК Заказ,
| Т.СерияНоменклатуры КАК СерияНоменклатуры,
| Т.СерийныйНомер КАК СерийныйНомер,
| Т.СрокГодности КАК СрокГодности,
| Т.Коэффициент КАК Коэффициент,
| СУММА(Т.Количество) КАК Количество
|ПОМЕСТИТЬ ВТ_СвернутыеТовары
|ИЗ
| &ТаблицаТоваров КАК Т
|
|СГРУППИРОВАТЬ ПО
| Т.Номенклатура,
| Т.Заказ,
| Т.СерияНоменклатуры,
| Т.СерийныйНомер,
| Т.СрокГодности,
| Т.Коэффициент";
Запрос.Выполнить();
// При необходимости выгружаем сгруппированные данные обратно в таблицу значений
Товары = МВТ.ПолучитьТаблицу("ВТ_СвернутыеТовары");
Преимущества подхода через запрос:
ТаблицаЗначений недоступен на тонком и веб-клиенте. Использование запросов позволяет легко выносить тяжелые вычисления на сторону сервера, не перегружая клиентское приложение передачей тяжелых коллекций данных.