В практике разработки на платформе 1С:Предприятие 8 часто встречается задача создания связанных табличных частей. Типичный пример: основная таблица с продукцией и две подчиненные таблицы (например, «Материалы» и «Технологии»), данные в которых меняются в зависимости от выбранной строки в главной таблице. Одной из основных проблем в этой схеме является корректный расчет итоговых показателей. Стандартный метод Итог() всегда возвращает сумму по всей коллекции строк, полностью игнорируя установленные визуальные фильтры (отборы).
В этой статье мы подробно разберем, как реализовать расчет суммы только по тем строкам, которые активны или отфильтрованы в данный момент, на примере обычных форм.
Рассмотрим ситуацию, когда в документе «Заявка» реализована связь между тремя табличными частями через уникальный идентификатор (ИД). При активации строки в первой таблице, в двух других устанавливается фильтр через свойство ОтборСтрок. Проблема заключается в том, что когда мы пытаемся получить итоговую сумму материалов или технологий для конкретной продукции, стандартный функционал выдает общую сумму по всему документу.
Давайте проанализируем фрагмент исходного кода, где настраивается фильтрация:
Процедура ЗаявкаПриАктивизацииСтроки(Элемент)
Данные = ЭлементыФормы.Заявка.ТекущаяСтрока;
Если Данные = Неопределено Тогда
Возврат;
КонецЕсли;
// Установка фильтра в подчиненных ТЧ
ЭлементыФормы.Материалы.ОтборСтрок.ИД.Значение = Данные.ИД;
ЭлементыФормы.Материалы.ОтборСтрок.ИД.Использование = Истина;
ЭлементыФормы.Технологии.ОтборСтрок.ИД.Значение = Данные.ИД;
ЭлементыФормы.Технологии.ОтборСтрок.ИД.Использование = Истина;
// Попытка получить итог (вернет сумму ВСЕХ строк ТЧ)
М = Материалы.Итог("Сумма");
Т = Технологии.Итог("Сумма");
КонецПроцедуры
Как мы видим, методы Материалы.Итог("Сумма") обращаются к объекту коллекции данных, а не к элементам управления формы. Чтобы решить задачу, нам нужно использовать методы, которые умеют работать с условиями или выборками.
Один из самых универсальных и корректных способов получить итоги по отфильтрованным строкам — это использование объекта ПостроительЗапроса. Его преимущество в том, что он может принимать в качестве источника данных табличную часть и учитывать те же самые объекты отбора, которые используются в визуальном интерфейсе ЭлементыФормы.
Разберем алгоритм реализации этого метода:
ПостроительЗапроса.ОписаниеИсточникаДанных.Пример программного кода для реализации этого подхода:
ПостроительЗапроса = Новый ПостроительЗапроса;
// Заявка - это имя вашей табличной части
ПостроительЗапроса.ИсточникДанных = Новый ОписаниеИсточникаДанных(Материалы);
// Копируем фильтры из формы в построитель
Для Каждого ЭлементОтбора из ЭлементыФормы.Материалы.ОтборСтрок Цикл
Если ЭлементОтбора.Использование Тогда
НовыйОтбор = ПостроительЗапроса.Отбор.Добавить(ЭлементОтбора.Имя);
ЗаполнитьЗначенияСвойств(НовыйОтбор, ЭлементОтбора);
КонецЕсли;
КонецЦикла;
// Получаем результат
ИтогТаблицаЗначений = ПостроительЗапроса.Результат.Выгрузить();
СуммаПоФильтру = ИтогТаблицаЗначений.Итог("Сумма");
Этот метод хорош тем, что он автоматически подхватывает любые сложные условия фильтрации, которые могут быть установлены программно или интерактивно.
Если связь между таблицами жестко задана через конкретный реквизит (в нашем случае ИД), то использование построителя запроса может быть избыточным. Более производительным и простым способом будет использование метода НайтиСтроки().
Рассмотрим, как это работает: метод НайтиСтроки() принимает структуру с параметрами поиска и возвращает массив строк, которые соответствуют условию. Мы можем просто перебрать этот массив и сложить значения в переменной-аккумуляторе.
Пример функции для расчета:
Функция ПолучитьСуммуПоИД(КоллекцияТЧ, ЗначениеИД)
Отбор = Новый Структура("ИД", ЗначениеИД);
МассивСтрок = КоллекцияТЧ.НайтиСтроки(Отбор);
ИтогСумма = 0;
Для Каждого СтрокаТЧ Из МассивСтрок Цикл
ИтогСумма = ИтогСумма + СтрокаТЧ.Сумма;
КонецЦикла;
Возврат ИтогСумма;
КонецФункции
Этот код работает значительно быстрее на небольших и средних объемах данных, так как не создает тяжелых объектов метаданных.
Существует еще один изящный способ, который позволяет избежать написания циклов. Если мы выгрузим табличную часть в объект ТаблицаЗначений, мы сможем воспользоваться встроенным методом Вычислить(), который поддерживает агрегатные функции и строковые фильтры.
Пример реализации:
ТЗ_Материалы = Материалы.Выгрузить();
// Формируем строку условия, например: "ИД = '5e2b...'"
СтрокаФильтра = "ИД = '" + ТекущийИД + "'";
Результат = ТЗ_Материалы.Вычислить("Сумма(Сумма)", СтрокаФильтра);
Важно: При использовании этого метода убедитесь, что значения внутри строки фильтра корректно экранированы, особенно если идентификатор является строкой.
Просто написать код расчета недостаточно. Чтобы пользователь всегда видел актуальные цифры, необходимо инициализировать пересчет в следующих событиях:
Чтобы результат вычислений выглядел профессионально, его стоит выводить в «подвал» (footer) табличного поля. Для этого выполните следующие действия:
Подвал (Footer) в значение Истина.
// Устанавливаем текст в подвал конкретной колонки
ЭлементыФормы.Материалы.Колонки.Сумма.ТекстПодвала = Формат(РассчитаннаяСумма, "ЧДЦ=2");
Это позволит пользователю видеть итоговую сумму именно там, где он привык её видеть в стандартных отчетах и списках.
Обратите внимание на использование УникальныйИдентификатор. Если вы создаете его только в памяти при открытии формы, то при закрытии документа без записи или при последующем открытии связи между таблицами могут быть потеряны. Для надежной работы необходимо добавить реквизит ИД (тип: Строка или УникальныйИдентификатор) непосредственно в структуру табличных частей документа в конфигураторе. Только в этом случае данные о связи «Продукция — Материалы» будут сохранены в базе данных навсегда.
Рассмотренные методы позволяют гибко управлять расчетами в интерфейсе 1С, обеспечивая точность данных даже при использовании сложных визуальных фильтров.