В процессе разработки отчетов в системе 1С:Предприятие программисты часто сталкиваются с необходимостью округления дробных чисел — например, при расчете цены за единицу товара или доли процента. Казалось бы, задача тривиальная, но она таит в себе подводные камни: от ошибок переполнения в запросе до расхождения итоговых сумм в отчетах. В этой статье мы подробно разберем, как работает функция ВЫРАЗИТЬ, чем она отличается от настройки формата в СКД и как избежать типичных ошибок при работе с СУБД.
Рассмотрим ситуацию: разработчик пытается рассчитать цену за единицу товара прямо в запросе, используя стандартную функцию ВЫРАЗИТЬ. Код выглядит следующим образом:
ВЫБРАТЬ
Продажи.Сумма / Продажи.Количество КАК ЦенаБезОкругления,
ВЫРАЗИТЬ(Продажи.Сумма / Продажи.Количество КАК ЧИСЛО(5, 2)) КАК ЦенаЗаЕдиницу
ИЗ
РегистрНакопления.Продажи КАК Продажи
При выполнении такого запроса система выдает критическую ошибку: «Microsoft SQL Server Native Client 11.0: Ошибка арифметического переполнения при преобразовании numeric к типу данных numeric. HRESULT=80040E57». Проанализируем причину её возникновения.
Ошибка кроется в понимании параметров типа ЧИСЛО(L, P) внутри функции ВЫРАЗИТЬ:
Таким образом, конструкция ЧИСЛО(5, 2) означает, что под всё число отводится 5 позиций, из которых 2 — десятичные. Это значит, что на целую часть остается всего 3 знака (5 минус 2). Максимальное число, которое может поместиться в такую структуру — 999,99. Если в результате деления суммы на количество получится число 1000 и более, SQL Server не сможет записать его в отведенную память и вернет ошибку переполнения.
Для решения этой проблемы необходимо всегда задавать разрядность «с запасом». В типовых конфигурациях 1С для финансовых показателей чаще всего используется ЧИСЛО(15, 2) или даже ЧИСЛО(31, 2), что позволяет избежать падения запроса при работе с большими суммами.
Многие начинающие разработчики путают механизмы реального изменения значения и визуального представления данных. Выясним, что происходит «под капотом» в каждом случае.
1. Функция ВЫРАЗИТЬ в запросе.
Эта функция работает на уровне СУБД и выполняет физическое преобразование типа. Она делает математическое округление. Это означает, что само значение числа в памяти компьютера меняется. Если вы потом будете использовать это поле в итогах СКД (например, суммировать округленную цену), результат будет рассчитан именно на основе округленных копеек.
2. Настройка «Формат» в СКД.
Если мы зайдем в настройки набора данных или в настройки оформления поля в СКД и установим формат «ЧДЦ=2» (Число Десятичных Цифр = 2), значение числа не изменится. Программа просто «спрячет» лишние знаки от пользователя при выводе в таблицу. В памяти же останется число с бесконечным количеством знаков после запятой (например, 53,103469...).
Почему это важно? Если вы выводите отчет, где много строк, и в каждой строке цена только «оформлена» через формат, то итоговая сумма внизу отчета может не совпасть с результатом ручного сложения видимых чисел на калькуляторе. Возникает эффект «плывущих копеек». Поэтому, если расчеты должны быть точными с точки зрения бухгалтерии, округление нужно делать через ВЫРАЗИТЬ в запросе или через функции в вычисляемых полях.
Если логика отчета позволяет производить расчеты после получения данных из БД, удобнее использовать вкладку «Вычисляемые поля» в СКД. Там нам доступен программный язык 1С, который богаче на математические функции, чем язык запросов.
Рассмотрим пример использования функции Окр() в выражении вычисляемого поля:
Окр(Сумма / Количество, 2, РежимОкругления.Окр15как20)
Преимущество этого метода в том, что мы можем явно управлять режимом округления (например, всегда округлять 1.5 до 2.0 или использовать банковское округление). В языке запросов через ВЫРАЗИТЬ доступно только стандартное математическое округление, принятое в SQL Server.
Иногда стоит задача не округлить число, а просто «отрезать» дробную часть. Например, из 53.9 получить 53. В языке запросов 1С нет функции FLOOR или ЦЕЛ. Как быть?
Существует несколько подходов:
ВЫРАЗИТЬ(МоеЧисло КАК ЧИСЛО(15, 0)). Но помните, что это округление! 53.9 превратится в 54.ВЫРАЗИТЬ(МоеЧисло - 0.5 КАК ЧИСЛО(15, 0)). Это заставит стандартное округление работать как отсечение целой части для большинства случаев.Цел(Цена) во вкладке «Вычисляемые поля».Проанализируем еще одну проблему, которая часто идет рука об руку с округлением в запросах. Если Продажи.Количество окажется равным нулю, запрос завершится аварийно. Чтобы этого избежать, всегда оборачивайте расчеты в конструкцию ВЫБОР:
ВЫБОР
КОГДА Продажи.Количество = 0 ТОГДА 0
ИНАЧЕ ВЫРАЗИТЬ(Продажи.Сумма / Продажи.Количество КАК ЧИСЛО(15, 2))
КОНЕЦ КАК Цена
Также не забывайте о значениях NULL, которые могут появиться при левых соединениях. Функция ВЫРАЗИТЬ(NULL КАК ЧИСЛО) вернет NULL, что может привести к ошибкам в дальнейших арифметических операциях. Используйте ЕСТЬNULL(), чтобы гарантировать наличие числового значения:
ВЫРАЗИТЬ(ЕСТЬNULL(Продажи.Сумма, 0) / ЕСТЬNULL(Продажи.Количество, 1) КАК ЧИСЛО(15, 2))
Для корректного округления в 1С придерживайтесь следующих правил:
ВЫРАЗИТЬ всегда указывайте достаточную общую длину числа (первый параметр), чтобы избежать ошибки HRESULT=80040E57.ВЫРАЗИТЬ в запросе, если вам нужно изменить само значение для последующих расчетов итогов.Окр().Рациональный выбор между округлением в запросе и оформлением в СКД позволит вам создавать точные, быстрые и устойчивые к ошибкам отчеты.