При разработке отчетов или форм списков в 1С:Предприятие 7.7 мы часто сталкиваемся с необходимостью фильтрации данных. Одна из типичных задач — получить остаток товара не по одному конкретному складу, а по списку выбранных складов (например, только по "рабочим" складам, исключая склады брака или транзитные). В более современных версиях платформы (8.х) мы привыкли к возможности передавать списки и массивы в параметры виртуальных таблиц. Однако в версии 7.7 есть свои нюансы.
Давайте подробно разберем, как ведет себя метод СводныйОстаток при передаче в него списка значений, и рассмотрим проверенные практикой способы решения этой задачи.
Для начала обратимся к синтаксису метода. Согласно документации, метод вызывается следующим образом:
СводныйОстаток(<Измерен1>, <Измерен2>..., <ИмяРесурса>)
Мы должны четко понимать, что платформа 7.7 ожидает в качестве параметров <Измерен...> конкретные скалярные значения, соответствующие типу измерения регистра. Если измерение имеет тип "Справочник.Склады", система ожидает ссылку на конкретный элемент справочника.
Что происходит, если передать список значений?
Если мы попытаемся передать объект типа СписокЗначений вместо элемента справочника, платформа не выдаст ошибку синтаксиса, но логика работы будет нарушена. Метод попытается найти измерение, которое равно самому объекту "СписокЗначений" (что невозможно для справочного типа) или приведет к неверному типу. В результате функция вернет 0 или пустое значение.
Таким образом, мы приходим к однозначному выводу: метод СводныйОстаток (как и метод Остаток) в 1С 7.7 не умеет работать со списками значений напрямую. Он работает только на строгое равенство переданному значению.
Если нам необходимо получить сумму остатков по списку складов штатными средствами (без использования прямых запросов), мы можем воспользоваться объектом Регистр и его методами фильтрации.
Рассмотрим алгоритм, предложенный участниками обсуждения. Он базируется на методе УстановитьЗначениеФильтра. Этот метод позволяет ограничить выборку при переборе итогов.
Обратите внимание: этот способ подходит для отчетов или обработок, где нужно один раз посчитать данные. Использовать его внутри формул списка (при отрисовке каждой строки) категорически не рекомендуется, так как это приведет к серьезному падению производительности ("тормозам").
Пример кода для расчета общего остатка по списку клиентов или складов:
// Создаем объект регистра
Рег = СоздатьОбъект("Регистр.ТоварныйКредит"); // Или Регистр.ОстаткиТоваров
// Устанавливаем фильтр.
// Параметр 2 означает "Отбирать" по вхождению в список
Рег.УстановитьЗначениеФильтра("Клиент", СписокКлиентов, 2);
// Выбираем итоги с учетом установленного фильтра
Рег.ВыбратьИтоги();
ВсегоОстаток = 0;
Пока Рег.ПолучитьИтог() = 1 Цикл
// Суммируем ресурсы по отфильтрованным записям
ВсегоОстаток = ВсегоОстаток + Рег.Сумма;
КонецЦикла;
Важные нюансы этого метода:
УстановитьЗначениеФильтра влияет на методы выборки (ВыбратьИтоги, ВыбратьДвижения).СводныйОстаток, если не используется механизм временного расчета регистров. То есть, просто установив фильтр и вызвав СводныйОстаток() без параметров, вы все равно получите полный итог по регистру, если не перебираете выборку.Часто задача стоит иначе: нужно в форме списка номенклатуры видеть остатки не по всем складам сразу, а, например, по трем конкретным "магазинам", игнорируя центральный склад. При этом пользователь хочет видеть данные в отдельных колонках или одной суммой.
Для реализации этого в форме списка справочника "Номенклатура" можно использовать текстовые колонки с формулой. Рассмотрим подход, который позволяет вывести данные по нескольким складам.
Алгоритм действий:
Пример функции, которую можно разместить в модуле формы списка:
// Функция вызывается из формулы колонки таблицы
// НомерСклада - индекс склада в нашем массиве 'Склады'
Функция ПоказатьОстаток(НомерСклада)
// Защита от ошибок индексации
Если (НомерСклада <= 0) Или (НомерСклада > Склады.РазмерСписка()) Тогда
Возврат "";
КонецЕсли;
ТекСклад = Склады.ПолучитьЗначение(НомерСклада);
// Получаем остаток конкретного товара на конкретном складе
// ПромФирма - если есть измерение по фирме
// ТекущийЭлемент() - ссылка на текущий товар в строке списка
ЗначениеОстатка = Регистр.ОстаткиТоваров.Остаток(ПромФирма, ТекущийЭлемент(), ТекСклад, "ОстатокТовара");
// Форматируем для красивого вывода
Возврат СокрЛП(Формат(ЗначениеОстатка, "Ч015.2"));
КонецФункции
В свойствах колонки в поле "Формула" мы пишем: ПоказатьОстаток(1), для второй колонки ПоказатьОстаток(2) и так далее.
Преимущества и недостатки:
Если в системе работает много пользователей, а база данных большая, штатные методы перебора или вызова Остаток() в цикле будут работать медленно. В таких случаях профессиональным стандартом для 1С 7.7 является использование прямых запросов к SQL (обычно с помощью внешней компоненты 1C++).
Именно этот способ выбрал автор вопроса как наиболее эффективный. Почему это работает быстрее?
IN. Он позволяет одним запросом выбрать записи, где поле (например, ID склада) входит в указанный список.Хотя полный код прямого запроса может быть громоздким, суть решения заключается в следующем:
RG...).WHERE мы передаем список ID наших складов.Это позволяет добиться мгновенного отклика интерфейса даже при сложной логике выбора складов (например, "показать остатки по всем магазинам региона, кроме закрытых на ремонт").
Подведем итог нашему исследованию:
Регистр.ОстаткиТоваров.СводныйОстаток (и аналогичные) не работает со списком значений в качестве параметра измерения. Он ожидает только конкретное значение.УстановитьЗначениеФильтра с последующим ВыбратьИтоги.