При разработке интерфейсов на управляемых формах 1С часто возникает задача: посчитать итоги не по всей таблице, а только по тем строкам, которые пользователь выделил мышкой или которые остались на экране после применения фильтра (отбора). Несмотря на кажущуюся простоту, начинающие разработчики часто сталкиваются с ошибками: сумма «задваивается», не учитываются скрытые строки или расчет происходит крайне медленно. В этой статье мы подробно разберем, как правильно реализовать этот функционал, проанализируем типичные ошибки и рассмотрим наиболее эффективные методы работы с данными форм.
Рассмотрим стандартную ситуацию. У нас есть табличная часть Материалы. Мы хотим, чтобы при нажатии кнопки программа находила все выделенные строки и суммировала значения в колонке Количество. Одной из главных ошибок является попытка суммировать данные в том же цикле, где происходит выделение строк, или использование индексов вместо идентификаторов. В управляемых формах коллекция ВыделенныеСтроки хранит не номера строк и не сами данные, а уникальные идентификаторы. Если мы просто добавим строку в выделение, не очистив предыдущий результат, или будем обращаться к данным по неверному ключу, мы получим некорректный результат.
Проанализируем типичный ошибочный код, где сумма «затирается» или считается только для последней строки:
// Ошибочный подход
Для Каждого Строка Из ВыделенныеСтроки Цикл
Сумма = Элементы.Материалы.ДанныеСтроки(Строка).Количество; // Здесь сумма перезаписывается, а не прибавляется
КонецЦикла;
СтрокаТЧ.Количество = Сумма;
Чтобы исправить это, мы должны использовать классический прием накопления суммы: Сумма = Сумма + ТекущееЗначение.
Для корректного расчета суммы выделенных строк нам необходимо выполнить три шага: получить массив идентификаторов выделенных строк, обойти его в цикле и найти данные каждой строки в объекте. Рассмотрим правильный пример реализации на клиенте:
&НаКлиенте
Процедура РассчитатьСуммуВыделенных()
// Получаем массив идентификаторов выделенных строк
ВыделенныеСтроки = Элементы.Материалы.ВыделенныеСтроки;
Сумма = 0;
// Проверяем, есть ли выделение
Если ВыделенныеСтроки.Количество() > 0 Тогда
Для Каждого ИдентификаторСтроки Из ВыделенныеСтроки Цикл
// Находим данные строки по её идентификатору в табличной части объекта
СтрокаДанных = Объект.Материалы.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаДанных <> Неопределено Тогда
Сумма = Сумма + СтрокаДанных.Количество;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// Выводим результат (например, в реквизит формы)
Объект.ИтоговаяСумма = Сумма;
КонецПроцедуры
Важный нюанс: использование метода НайтиПоИдентификатору предпочтительнее, чем ДанныеСтроки(), так как последний метод возвращает данные только для тех строк, которые в данный момент видимы на экране (отрисованы). Если пользователь выделил 100 строк и прокрутил список, ДанныеСтроки() для части из них вернет Неопределено.
Иногда задача стоит иначе: нужно посчитать сумму всех строк, которые попали в отбор, независимо от того, выделил их пользователь или нет. Для этого в платформе 1С существует полезный метод ПроверитьСтроку(). Он позволяет выяснить, соответствует ли строка текущим настройкам фильтрации в элементе формы.
Выясним, как это реализовать:
&НаКлиенте
Процедура СуммаПоОтбору()
СуммаПоОтбору = 0;
// Перебираем ВСЕ строки табличной части
Для Каждого СтрокаМатериала Из Объект.Материалы Цикл
// Проверяем, видна ли эта строка пользователю с учетом фильтров
Если Элементы.Материалы.ПроверитьСтроку(СтрокаМатериала.ПолучитьИдентификатор()) Тогда
СуммаПоОтбору = СуммаПоОтбору + СтрокаМатериала.Количество;
КонецЕсли;
КонецЦикла;
Сообщить("Сумма отобранных строк: " + СуммаПоОтбору);
КонецПроцедуры
Этот способ крайне удобен, так как нам не нужно программно анализировать сложную структуру ФиксированнаяСтруктура, которая лежит в свойстве ОтборСтрок.
Если мы работаем не с табличной частью документа, а с Динамическим списком (например, список всех заказов), то данные на клиенте отсутствуют в полном объеме. В этом случае попытка получить данные через ПолучитьОбъект() в цикле на сервере приведет к катастрофическому падению производительности.
Рассмотрим, как проанализировать ситуацию и оптимизировать расчет:
Пример эффективного серверного кода:
&НаСервере
Функция ПолучитьСуммуВыделенныхНаСервере(МассивИдентификаторов)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СУММА(ДокументПродажи.СуммаДокумента) КАК Итого
|ИЗ
| Документ.Продажа КАК ДокументПродажи
|ГДЕ
| ДокументПродажи.Ссылка В(&МассивИдентификаторов)";
Запрос.УстановитьПараметр("МассивИдентификаторов", МассивИдентификаторов);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Итого;
КонецЕсли;
Возврат 0;
КонецФункции
Разберем еще несколько важных моментов, которые помогут сделать ваш код надежным:
1. Очистка выделения. Если вы программно управляете выделением строк (например, по нажатию кнопки «Выделить все по материалу»), обязательно вызывайте Элементы.Таблица.ВыделенныеСтроки.Очистить() перед добавлением новых ID. В противном случае старые выделенные строки останутся в массиве, и сумма «задвоится» или будет содержать лишние данные.
2. Событие обновления. Расчет суммы логично привязать к событию ПриАктивизацииСтроки. Однако помните, что оно срабатывает очень часто. Чтобы интерфейс не «тормозил», можно использовать ПодключитьОбработчикОжидания. Это позволит запускать расчет суммы с небольшой задержкой (например, 0.2 секунды) после того, как пользователь закончил навигацию по списку.
3. Использование подвала. Если вам нужно видеть итог по всей таблице, не забывайте про стандартное свойство ПутьКДаннымПодвала и АвтоматическийИтог у колонки таблицы. Это работает без единой строчки кода для всей таблицы в целом, но для выделенных строк придется использовать описанные выше программные методы.
Таким образом, мы выяснили, что ключ к правильному расчету — это понимание разницы между индексами и идентификаторами, а также выбор правильного места для вычислений (клиент для ТЧ, сервер для списков). Используя методы НайтиПоИдентификатору и ПроверитьСтроку, вы обеспечите корректную работу программы в любых сценариях работы пользователя.