Как устранить расхождения при округлении итогов в запросах 1С?

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

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

Выясним причину: почему итоги «не бьются»?

Первопричина кроется в разнице между хранимой точностью данных и точностью представления. В типовых или доработанных конфигурациях ресурсы регистров (например, СуммаПродажная) могут иметь точность более 2 знаков после запятой (например, 3 или 4 знака). Это необходимо для минимизации погрешностей при сложных расчетах, таких как распределение скидок или расчет себестоимости.

Разберем ситуацию на примере. Допустим, у нас есть три записи в регистре:

  1. Товар А: 10.444 руб.
  2. Товар Б: 10.444 руб.
  3. Товар В: 10.444 руб.

Проанализируем расчет итогов без предварительного округления:

  1. Если мы просто выводим их с округлением до 2 знаков, в отчете мы увидим три строки по 10.44 руб. Сумма этих строк визуально равна 31.32 руб.
  2. Запрос при расчете секции ИТОГИ сначала сложит точные значения: 10.444 + 10.444 + 10.444 = 31.332 руб.
  3. При выводе итога в макет число 31.332 будет округлено до 31.33 руб.

В результате пользователь видит: 10.44 + 10.44 + 10.44 = 31.33. Разница в 1 копейку возникла из-за того, что итоги считались по «хвостам», которые были скрыты при выводе детальных записей. Использование функции Формат() в макете только маскирует проблему, но не решает её, так как она меняет лишь визуальное представление, а не само число в выборке запроса.

Решение 1: Использование функции ВЫРАЗИТЬ в списке полей

Самый простой способ, который приходит на ум — это использование функции ВЫРАЗИТЬ() непосредственно в тексте запроса. Посмотрим на пример кода:


ВЫБРАТЬ
    ТоварыВРозницеОстатки.Склад КАК Склад,
    ТоварыВРозницеОстатки.Номенклатура КАК Номенклатура,
    ВЫРАЗИТЬ(ТоварыВРозницеОстатки.СуммаПродажнаяОстаток КАК ЧИСЛО(15, 2)) КАК СуммаОкругленная
ИЗ
    РегистрНакопления.ТоварыВРознице.Остатки КАК ТоварыВРозницеОстатки
ИТОГИ
    СУММА(СуммаОкругленная)
ПО
    ОБЩИЕ,
    Склад

Проанализируем работу этого механизма. Функция ВЫРАЗИТЬ() на уровне СУБД выполняет приведение типа, что приводит к округлению числа. Однако стоит помнить о важном нюансе: СУБД (например, MS SQL Server) часто используют так называемое «банковское округление» (округление до ближайшего четного), если число находится ровно посередине (например, 0.5). Это может отличаться от привычного нам арифметического округления.

Хотя в простом запросе это может сработать, практика показывает, что при сложных вложенных запросах или использовании виртуальных таблиц остатков, механизм ИТОГИ иногда может обращаться к исходным полям до их преобразования, особенно если поля участвуют в выражениях. Поэтому для стопроцентной гарантии рекомендуется второй метод.

Решение 2: Использование временных таблиц (Наиболее надежный метод)

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

  1. В первом запросе пакета мы выбираем данные и принудительно округляем их с помощью ВЫРАЗИТЬ().
  2. Результат сохраняем во временную таблицу. На этом этапе в базе данных (в таблице tempdb) физически создаются записи, где чисел с 3-м и 4-м знаками после запятой просто не существует.
  3. Во втором запросе мы выбираем данные из временной таблицы и применяем конструкцию ИТОГИ.

Посмотрим на реализацию этого алгоритма:


ВЫБРАТЬ
    ТоварыВРозницеОстатки.Склад КАК Склад,
    ТоварыВРозницеОстатки.Номенклатура КАК Номенклатура,
    ВЫРАЗИТЬ(ТоварыВРозницеОстатки.СуммаПродажнаяОстаток КАК ЧИСЛО(15, 2)) КАК Сумма
ПОМЕСТИТЬ ВТ_Остатки
ИЗ
    РегистрНакопления.ТоварыВРознице.Остатки КАК ТоварыВРозницеОстатки
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Остатки.Склад,
    ВТ_Остатки.Номенклатура,
    ВТ_Остатки.Сумма
ИЗ
    ВТ_Остатки КАК ВТ_Остатки
ИТОГИ
    СУММА(Сумма)
ПО
    ОБЩИЕ,
    Склад,
    Номенклатура

В данном случае расчет СУММА(Сумма) в секции ИТОГИ будет гарантированно использовать уже округленные значения из ВТ_Остатки. Математическая погрешность исключается, так как суммируются именно те значения (с 2 знаками), которые попадут в отчет.

Особенности работы с Системой компоновки данных (СКД)

Если вы разрабатываете отчет на СКД, ситуация может быть сложнее. Проанализируем работу ресурсов. По умолчанию СКД рассчитывает итоги по детальным записям. Если в выражении ресурса указано Сумма(СуммаПродажная), СКД возьмет все копейки из базы, сложит их и только в конце применит оформление.

Чтобы итоги в СКД сходились, можно использовать следующие подходы:

  1. Округлять данные в самом запросе (наборе данных) через временную таблицу, как описано выше.
  2. Использовать специальные функции в выражении ресурса. Например, чтобы заставить СКД сначала округлить каждую строку, а потом сложить, используется конструкция: Сумма(ВычислитьВыражениеСГруппировкойТаблица("Окр(Сумма, 2)", "Номенклатура")).

Важные рекомендации

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

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

← На главную