Как рассчитать сумму по колонке в табличной части, если строки являются ссылками на другие документы

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда в табличной части одного документа (например, «Задание на оплату») содержатся ссылки на другие документы (например, «Заявка на оплату»). Для удобства пользователя мы выводим реквизиты связанного документа (сумму, дату, контрагента) прямо в список, перетаскивая их «через точку» в редакторе формы. Однако при попытке программно подсчитать итог по такой колонке стандартный метод Итог() выдает ошибку или возвращает ноль. Разберем подробно, почему так происходит и какими способами можно решить эту задачу.

Почему метод Итог() не работает для связанных данных

Прежде всего, проанализируем внутреннее устройство табличной части. Метод Объект.ИмяТабличнойЧасти.Итог("ИмяРеквизита") работает только с теми данными, которые физически существуют в коллекции (таблице) объекта. Когда мы вытаскиваем на форму поле Счет.Сумма, мы создаем лишь визуальное представление. В самой табличной части «Задание на оплату» физически хранится только ссылка на документ «Заявка на оплату», а колонки «Сумма» в структуре базы данных для этой таблицы не существует.

Следовательно, для получения итогов нам необходимо либо программно обойти все ссылки и суммировать их значения, либо изменить архитектуру хранения данных. Рассмотрим все варианты по порядку.

Решение 1: Программный пересчет циклом на сервере

Самый быстрый в реализации способ — это создание серверной функции, которая будет перебирать строки табличной части и обращаться к реквизитам связанных документов. Рассмотрим этот метод детально.

Поскольку обращение к реквизитам через ссылку (Строка.Счет.Сумма) требует обращения к базе данных, такой код должен выполняться в серверном контексте. На клиенте ссылка — это лишь идентификатор, и система «не знает» о значениях ее реквизитов без обращения к серверу.

Реализуем обработчик события ПриИзменении для табличной части:


&НаКлиенте
Процедура СчетаПриИзменении(Элемент)
    // Вызываем серверную функцию для расчета суммы
    Объект.СуммаДокумента = ПолучитьСуммуНаСервере();
КонецПроцедуры

&НаСервере
Функция ПолучитьСуммуНаСервере()
    ПеременнаяСумма = 0;
    // Обходим коллекцию строк табличной части
    Для Каждого СтрИзСписка Из Объект.Счета Цикл
        // Проверяем, заполнена ли ссылка, чтобы избежать ошибок
        Если ЗначениеЗаполнено(СтрИзСписка.Счет) Тогда
            // Обращаемся к реквизиту связанного документа через точку
            ПеременнаяСумма = ПеременнаяСумма + СтрИзСписка.Счет.Сумма;
        КонецЕсли;
    КонецЦикла;
    Возврат ПеременнаяСумма;
КонецФункции

Важный нюанс: Обращение «через точку» в цикле создает так называемую проблему «N+1 запроса». Если в таблице 50 строк, система выполнит 50 микро-запросов к базе данных. Для небольших таблиц это допустимо, но для нагруженных систем это не лучший вариант.

Решение 2: Оптимизация с использованием запроса

Чтобы избежать многократных обращений к базе данных в цикле, профессиональнее использовать механизм запросов. Мы передадим все ссылки из таблицы в запрос, и сервер 1С одним махом получит сумму по всем документам.

Рассмотрим пример реализации такой функции:


&НаСервере
Функция ПолучитьСуммуЗапросом()
    // Выгружаем все ссылки из колонки "Счет" в массив
    МассивСсылок = Объект.Счета.ВыгрузитьКолонку("Счет");
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    СУММА(ЗаявкаНаОплату.Сумма) КАК ОбщаяСумма
        |ИЗ
        |    Документ.ЗаявкаНаОплату КАК ЗаявкаНаОплату
        |ГДЕ
        |    ЗаявкаНаОплату.Ссылка В(&МассивСсылок)";
    
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.ОбщаяСумма;
    КонецЕсли;
    
    Возврат 0;
КонецФункции

Этот метод работает значительно быстрее при большом количестве строк в табличной части, так как минимизирует количество транзакций между сервером приложений и сервером базы данных.

Решение 3: Хранение суммы непосредственно в табличной части

Если производительность формы критична, или если сумма в документе-заявке может измениться после того, как она была включена в задание на оплату, рекомендуется использовать дублирование данных. Для этого в табличную часть «Счета» добавляется реальный реквизит Сумма с типом Число.

Алгоритм работы в этом случае будет следующим:

  1. При выборе документа в колонку Счет (событие СчетаСчетПриИзменении) программно копируем значение суммы из заявки в строку табличной части.
  2. Для отображения итогов теперь можно использовать стандартный и самый быстрый метод: Объект.Счета.Итог("Сумма").

Пример кода для обработки выбора строки:


&НаКлиенте
Процедура СчетаСчетПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Счета.ТекущиеДанные;
    Если ТекущаяСтрока <> Неопределено Тогда
        // Получаем данные документа-ссылки на сервере
        ТекущаяСтрока.Сумма = ПолучитьДанныеЗаявки(ТекущаяСтрока.Счет);
        // Теперь Итог() будет работать корректно
        Объект.СуммаДокумента = Объект.Счета.Итог("Сумма");
    КонецЕсли;
КонецПроцедуры

Отображение итогов в интерфейсе (подвал таблицы)

Просто посчитать сумму в реквизит «СуммаДокумента» часто недостаточно для удобства пользователя. Рассмотрим, как вывести это значение красиво в «подвал» (футер) самой таблицы.

Для этого выполним следующие действия:

  1. В свойствах элемента формы Таблица (вашей ТЧ) установим галочку Подвал.
  2. У конкретной колонки, где мы хотим видеть итог, найдем свойство ТекстПодвала.
  3. Программно будем обновлять этот текст при каждом пересчете.

Пример обновления визуального итога:


&НаКлиенте
Процедура ОбновитьИтогиНаФорме()
    СуммаИтого = Объект.Счета.Итог("Сумма");
    // Устанавливаем форматированный текст в подвал колонки
    Элементы.СчетаСчетСумма.ТекстПодвала = Формат(СуммаИтого, "ЧДЦ=2");
КонецПроцедуры

Когда нужно пересчитывать сумму?

Разберем типичную ошибку: пересчет только в событии ПриИзменении всей таблицы. Для корректной работы приложения сумма должна актуализироваться в следующих случаях:

Проанализируем ситуацию: если мы используем первый способ (расчет «через точку» без хранения данных в ТЧ), то вызов пересчета в событии ПриОткрытии обязателен, иначе поле суммы будет пустым до тех пор, пока пользователь что-нибудь не изменит в таблице.

Итоги и рекомендации

Подведем итог нашему исследованию. Выбор метода зависит от ваших задач:

  1. Если строк мало и архитектура не позволяет менять состав реквизитов — используйте простой цикл на сервере.
  2. Если важна скорость работы и строк много — используйте Запрос с передачей массива ссылок.
  3. Если вы проектируете систему «с нуля» — лучше добавить реквизит Сумма в ТЧ. Это избавит от лишних серверных вызовов при каждом просмотре документа и позволит использовать встроенный высокопроизводительный метод Итог().

Помните, что работа в управляемых формах требует четкого разделения контекстов: данные объекта доступны везде, но значения «через точку» от ссылок — только на сервере. Правильное управление серверными вызовами — залог быстрого и отзывчивого интерфейса вашей программы.

← На главную