Как подсчитать итоги по колонке в табличной части или таблице значений и сравнить их с итогом документа

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

В практике разработчика 1С часто возникает задача проверки целостности данных. Одной из самых распространенных ситуаций является сверка итоговой суммы в «шапке» документа с фактической суммой всех строк в его табличной части. Несмотря на кажущуюся простоту, здесь есть несколько подходов, каждый из которых оптимален для своих условий. Разберем подробно, как реализовать этот расчет программно и с помощью языка запросов.

Способ 1: Использование встроенного метода Итог()

Если нам необходимо получить сумму по колонке непосредственно в коде при работе с объектом (например, в модуле объекта перед записью или в форме), самым быстрым и простым способом будет использование метода Итог(). Этот метод доступен как для табличных частей, так и для объектов типа ТаблицаЗначений.

Рассмотрим пример, когда нам нужно быстро проверить сумму прямо в обработчике события:


// Получаем итог по колонке "Сумма" табличной части "Товары"
ВсегоПоСтрокам = Объект.Товары.Итог("Сумма");

// Сравниваем с реквизитом шапки
Если ВсегоПоСтрокам <> Объект.СуммаДокумента Тогда
    Сообщить("Внимание! Сумма по строкам (" + ВсегоПоСтрокам + ") не совпадает с суммой документа!");
КонецЕсли;

Важные нюансы метода Итог():

  1. Тип данных: Метод работает только с колонками, имеющими числовой тип. Если в колонке встретится NULL или Неопределено (что может случиться в таблицах значений, созданных вручную), метод может отработать некорректно или вызвать ошибку.
  2. Актуальность данных: Итог() считает данные, которые находятся в оперативной памяти (в коллекции). Если пользователь изменил данные на форме, но еще не нажал «Записать», Итог() покажет новые значения, в то время как запрос к базе данных вернет старые.

Способ 2: Использование языка запросов для поиска расхождений

Иногда требуется проверить не один конкретный документ, а найти все документы в базе, где сумма шапки не совпадает с суммой строк. В этом случае использование циклов и метода Итог() будет крайне неэффективным. Нам поможет язык запросов.

Разберем ситуацию, когда нам нужно выбрать документы «Перемещение товаров», в которых допущена ошибка в расчете итогов. Проанализируем правильный подход к построению такого запроса. Мы будем обращаться не к самому документу, а к его табличной части, группируя строки по ссылке.


ВЫБРАТЬ
    ПеремещениеТоваровТовары.Ссылка КАК Ссылка,
    ПеремещениеТоваровТовары.Ссылка.СуммаДокумента КАК СуммаВШапке,
    СУММА(ПеремещениеТоваровТовары.Сумма) КАК СуммаПоСтрокам
ПОМЕСТИТЬ ВТ_ИтогиДокументов
ИЗ
    Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
СГРУППИРОВАТЬ ПО
    ПеремещениеТоваровТовары.Ссылка,
    ПеремещениеТоваровТовары.Ссылка.СуммаДокумента
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ИтогиДокументов.Ссылка,
    ВТ_ИтогиДокументов.СуммаВШапке,
    ВТ_ИтогиДокументов.СуммаПоСтрокам
ИЗ
    ВТ_ИтогиДокументов КАК ВТ_ИтогиДокументов
ГДЕ
    ВТ_ИтогиДокументов.СуммаВШапке <> ВТ_ИтогиДокументов.СуммаПоСтрокам

Рассмотрим по шагам, что происходит в этом запросе:

  1. Мы выбираем данные из таблицы Документ.ПеремещениеТоваров.Товары. Это позволяет нам сразу получить доступ ко всем строкам всех документов.
  2. Мы используем функцию СУММА() для колонки с числами.
  3. Мы обязательно применяем секцию СГРУППИРОВАТЬ ПО, указывая ссылку на документ и поле из шапки (через точку от ссылки). Это необходимо для работы агрегатных функций.
  4. Результат мы помещаем во временную таблицу для удобства последующего сравнения.
  5. В финальной выборке мы накладываем фильтр ГДЕ, чтобы оставить только те записи, где суммы различаются.

Типичные ошибки при использовании конструктора запросов

Часто начинающие разработчики пытаются выбрать таблицу документа и добавить в список полей его табличную часть как вложенную таблицу. Это приводит к созданию структуры, с которой крайне сложно работать в плане итогов и группировок. Выясним причину, почему так делать не стоит:

Во-первых, вложенные таблицы в запросах (когда ТЧ выбирается как поле) — это устаревший механизм, который перегружает результат запроса. Во-вторых, конструктор запроса не позволит вам эффективно выполнить группировку и агрегирование внутри такой вложенной структуры для сравнения с внешним полем. Рекомендуется всегда выбирать данные напрямую из таблицы табличной части, как показано в примере выше.

Оптимизация запроса через секцию ИМЕЮЩИЕ

Проанализируем возможность сокращения кода. Если нам не нужно использовать временные таблицы, мы можем воспользоваться конструкцией ИМЕЮЩИЕ (аналог HAVING в SQL). Она позволяет фильтровать результат уже после выполнения группировки.


ВЫБРАТЬ
    ПеремещениеТоваровТовары.Ссылка КАК Ссылка,
    МАКСИМУМ(ПеремещениеТоваровТовары.Ссылка.СуммаДокумента) КАК СуммаДокумента,
    СУММА(ПеремещениеТоваровТовары.Сумма) КАК СуммаПоСтрокам
ИЗ
    Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
СГРУППИРОВАТЬ ПО
    ПеремещениеТоваровТовары.Ссылка
ИМЕЮЩИЕ
    МАКСИМУМ(ПеремещениеТоваровТовары.Ссылка.СуммаДокумента) <> СУММА(ПеремещениеТоваровТовары.Сумма)

Здесь мы избавляемся от лишнего шага с временной таблицей, что делает код более компактным и читаемым.

Обработка пустых значений и точность расчета

При работе с суммами важно учитывать два технических момента: наличие пустых значений (NULL) и точность округления.

Функция ЕСТЬNULL: Если вы выполняете левое соединение таблиц для сверки, всегда используйте ЕСТЬNULL(Сумма, 0). В противном случае, если в документе вообще нет строк, сравнение числа с NULL не даст ожидаемого результата «Ложь» или «Истина», и ошибка может быть пропущена.

Округление: В 1С суммы в табличных частях могут иметь большую точность, чем итог в шапке (особенно если есть автоматические скидки или расчеты по налогам). Чтобы избежать ложных срабатываний из-за разницы в 0.0000001 копейки, в условиях сравнения полезно использовать функцию ОКРУГЛИТЬ():


ГДЕ
    ОКРУГЛИТЬ(ВТ_ИтогиДокументов.СуммаВШапке, 2) <> ОКРУГЛИТЬ(ВТ_ИтогиДокументов.СуммаПоСтрокам, 2)

Подводя итог, мы видим, что для оперативной проверки в коде лучше всего подходит ТабЧасть.Итог("Сумма"), а для массового анализа данных и поиска ошибок — грамотно сконструированный запрос с группировкой по ссылке документа.

← На главную