Как округлить число в запросе на языке 1С

· 9 мин

Содержание

Функции Окр() в языке запросов 1С нет — она есть только во встроенном языке, то есть в модулях. Если вписать Окр(Сумма, 2) прямо в текст запроса, конструктор подчеркнёт это как ошибку, и запрос не выполнится. В запросе округляют оператором ВЫРАЗИТЬ с типом ЧИСЛО: например, ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2)).

На этом чаще всего спотыкаются: язык запросов и встроенный язык — разные языки с разным набором функций. То, что привычно в модуле (Окр, Цел, НачалоМесяца), в запросе называется иначе или делается через ВЫРАЗИТЬ, НАЧАЛОПЕРИОДА и РАЗНОСТЬДАТ.

Ниже — как округлять суммы до копеек, как получить целую часть, как округлять даты, почему итог по строкам расходится с итогом по сумме на копейку и какие ошибки точности возникают у типа Число.

Чем округляют в запросе: оператор ВЫРАЗИТЬ

Основной инструмент — ВЫРАЗИТЬ(Выражение КАК ЧИСЛО(Разрядность, Точность)):

  • Разрядность — общее число значащих цифр (и до, и после запятой).
  • Точность — сколько знаков оставить после запятой.

Сокращая точность, платформа округляет значение. Пример — поделить сумму на три равные части и округлить до копеек:

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

Если СуммаДокумента = 100, то 100 / 3 = 33,3333…, а ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) даёт 33,33.

Разрядность берите с запасом под целую часть. ВЫРАЗИТЬ(123456.78 КАК ЧИСЛО(5, 2)) не помещается в пять знаков — целая часть 123456 длиннее, и запрос вернёт ошибку переполнения. Для денежных сумм безопасно ЧИСЛО(15, 2).

Чем это отличается от округления в модуле, удобно держать в одной таблице:

ЗадачаВстроенный язык (модуль)Язык запросов
Округлить до копеекОкр(Сумма, 2)ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2))
Целая частьЦел(Число)ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 0)) (но округляет)
Округлить дату до месяцаНачалоМесяца(Дата)НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ)
Число дней между датами—РАЗНОСТЬДАТ(Д1, Д2, ДЕНЬ)

Режим округления «0,5 вверх» или «0,5 вниз» у ВЫРАЗИТЬ в редких случаях зависит от СУБД (файловая база, MS SQL, PostgreSQL). Если расхождение в копейку критично — округляйте в модуле функцией Окр с явным третьим параметром, а не в запросе.

Если округлять в модуле: функция Окр и её режимы

Когда данные уже выгружены в таблицу значений или обрабатываются построчно, округляют во встроенном языке. Здесь живёт та самая Окр, которой нет в запросе:

Окр(1234.5678, 2);  // 1234.57 — до двух знаков
Окр(1234.5, -2);    // 1200    — до сотен (отрицательная разрядность)
  • Первый параметр — число.
  • Второй — разрядность: положительная округляет до знаков после запятой, отрицательная — до десятков, сотен, тысяч.
  • Третий — режим: РежимОкругления.Окр15как20 (0,5 округляется вверх) или РежимОкругления.Окр15как10 (0,5 отбрасывается вниз).

Режим задавайте явно. От значения по умолчанию лучше не зависеть: в расчётах налогов и зарплаты разница в сторону округления 0,5 — это реальные копейки в проводках. Окр(2.5, 0, РежимОкругления.Окр15как20) вернёт 3, а с режимом Окр15как10 — 2.

Эта функция и есть «основной инструмент» округления у разработчика. В запросе её заменяет ВЫРАЗИТЬ, но вся логика с режимами и отрицательной разрядностью доступна только в модуле.

Как получить целую часть числа без функции Цел

Функции Цел() в запросе тоже нет. Целую часть получают тем же ВЫРАЗИТЬ с нулевой точностью:

ВЫРАЗИТЬ(Товары.Количество КАК ЧИСЛО(15, 0))

Здесь важная ловушка: ЧИСЛО(15, 0) округляет, а не отбрасывает дробную часть. ВЫРАЗИТЬ(2.7 КАК ЧИСЛО(15, 0)) даст 3, а не 2. Если нужно именно отбросить дробь (как делает Цел в модуле), в запросе чистого способа нет — для положительных чисел применяют приём со сдвигом:

ВЫРАЗИТЬ(Товары.Количество - 0.5 КАК ЧИСЛО(15, 0))

Он работает для положительных значений и приблизительно: на точной половине (ровно x,5) результат непредсказуем. Если нужна честная целая часть с учётом знака — считайте её во встроенном языке функцией Цел, а не в запросе. Во встроенном языке Цел(1234.99) вернёт 1234, а Цел(-1234.99) — -1234 (отбрасывание к нулю).

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

Округление дат: начало периода и разность дат

«Округлить» дату — это привести её к началу или концу дня, месяца, квартала, года. В запросе для этого есть НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА:

ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Док.Дата, МЕСЯЦ)  КАК НачалоМесяца,
    КОНЕЦПЕРИОДА(Док.Дата, КВАРТАЛ) КАК КонецКвартала,
    НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ)   КАК НачалоДня
ИЗ
    Документ.РеализацияТоваровУслуг КАК Док

Доступные периоды: ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД, а для времени — ЧАС, МИНУТА, СЕКУНДА. НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) обнуляет время и оставляет полночь — удобно, когда в поле записана дата со временем, а сравнивать нужно только по дню.

Разницу между датами в запросе считает РАЗНОСТЬДАТ (а не выдуманное «ПолучитьПериодМежду», которое иногда встречается в примерах):

РАЗНОСТЬДАТ(ДатаНачала, ДатаОкончания, ДЕНЬ)

Третий параметр задаёт единицу: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ, КВАРТАЛ, ГОД. Результат — целое число периодов. Например, РАЗНОСТЬДАТ в днях между 01.01 и 31.01 вернёт 30.

РАЗНОСТЬДАТ(..., МЕСЯЦ) считает границы календарных месяцев, а не «ровно 30 дней». Между 31 января и 1 февраля функция вернёт 1 месяц, хотя прошли всего сутки. Для расчёта стажа или срока это нужно учитывать.

Тип Число, разрядность и потеря точности

У типа Число в запросе две характеристики — разрядность (сколько всего цифр) и точность (сколько после запятой). Проблемы точности возникают в двух местах.

Деление даёт длинный хвост. Результат деления в запросе может нести много знаков после запятой. Если потом сравнивать такое число на равенство, сравнение не сработает: 10 / 3 * 3 не всегда ровно 10. Лечение — приводить результат к нужной точности ВЫРАЗИТЬ перед сравнением или группировкой.

Та же причина ломает соединения и группировки по вычисляемому числу. Когда в условии соединения или в СГРУППИРОВАТЬ ПО стоит результат деления, строки с «почти равными» значениями не склеятся — платформа видит 33,333333 и 33,333334 как разные. Приводите ключ группировки к фиксированной точности заранее.

Переполнение разрядности. Если задать слишком маленькую разрядность, значение в неё не влезет. ВЫРАЗИТЬ(СуммаМиллионы КАК ЧИСЛО(6, 2)) на сумме 1 234 567,89 вернёт ошибку. Поэтому для денег берут ЧИСЛО(15, 2), для количеств с дробями — ЧИСЛО(15, 3).

Если поле может быть NULL (например, после левого соединения), оберните его в ЕСТЬNULL(Поле, 0) до округления. ВЫРАЗИТЬ(NULL КАК ЧИСЛО(15, 2)) вернёт NULL, и дальше по строке посыпятся пустые итоги.

Короткий чек перед тем, как доверять числу из запроса:

  • деление и проценты — обязательно через ВЫРАЗИТЬ до нужной точности;
  • суммы для сравнения на равенство — к одной точности с обеих сторон;
  • возможные NULL — закрыты ЕСТЬNULL;
  • разрядность с запасом под самую крупную ожидаемую сумму.

Округлять до суммирования или после: разница в копейках

Самая коварная тема. СУММА — агрегатная функция, и порядок «сначала округлить, потом сложить» против «сначала сложить, потом округлить» даёт разный результат.

// Вариант А: округляем каждую строку, затем суммируем
СУММА(ВЫРАЗИТЬ(Строки.Цена * Строки.Количество КАК ЧИСЛО(15, 2)))

// Вариант Б: суммируем, затем округляем итог
ВЫРАЗИТЬ(СУММА(Строки.Цена * Строки.Количество) КАК ЧИСЛО(15, 2))

Пусть в документе три строки по 10,005:

  • Вариант А: каждая строка округляется до 10,01, сумма трёх — 30,03.
  • Вариант Б: сумма 30,015 округляется до 30,02.

Расхождение — копейка. В печатных формах и проводках это всплывает постоянно.

То же касается СРЕДНЕЕ: среднее почти всегда получается дробным, и округлять его нужно после агрегации, а не внутри. Округлив каждое слагаемое до среднего, вы исказите сам результат.

Правило: для первички округляйте построчно (как в документе) и только потом считайте итог — тогда итог совпадёт с суммой строк, которую видит пользователь. Для аналитических отчётов, где строки не показывают, допустимо округлять итог.

Проверьте себя
Что вернёт `ВЫРАЗИТЬ(2.7 КАК ЧИСЛО(15, 0))`?
Можно ли написать `Окр()` прямо в тексте запроса?
Что вернёт `РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ)`?

Пересчёт валюты и округление до копеек

При пересчёте валютных сумм в рубли округление до копеек обязательно — иначе в проводках появятся доли копейки. Формула простая: сумма × курс ÷ кратность, и всё это через ВЫРАЗИТЬ:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Строки.СуммаВалюта * Курсы.Курс / Курсы.Кратность
             КАК ЧИСЛО(15, 2)) КАК СуммаРуб
ИЗ
    ...
  • Курс — сколько рублей за единицу (или за кратность) валюты.
  • Кратность — за сколько единиц валюты указан курс. Для большинства валют 1, но для валют с мелким номиналом курс дают за 100 единиц — тогда делить нужно на 100.

Пример: 100 USD по курсу 92,3456 и кратности 1 → 100 × 92,3456 = 9234,56 ₽. Округление до копеек ничего не меняет, сумма уже ровная.

Для валюты с кратностью 100 формула учитывает делитель. Пусть курс дан как 62,50 за 100 единиц, тогда 1000 × 62,50 / 100 = 625,00 ₽. Если забыть про кратность и умножить напрямую, сумма вырастет в сто раз — частая причина завышенных валютных остатков в отчётах.

Отдельно — выделение НДС. НДС из суммы с налогом по ставке 20% считают как сумма × 20 ÷ 120, и результат округляют до копеек:

ВЫРАЗИТЬ(Строки.Сумма * 20 / 120 КАК ЧИСЛО(15, 2))

Для 1234,56 это 1234,56 × 20 / 120 = 205,76. Если НДС считать по каждой строке и округлять построчно, итоговый НДС по документу должен сходиться с суммой построчных — поэтому порядок округления из предыдущего раздела здесь так же важен, как в первичке.

Курсовые разницы и пересчёт на дату — отдельная тема учёта. В запросе вы только корректно округляете, а правило пересчёта (на дату документа, оплаты или отчётности) определяет учётная политика.

Частые ошибки при округлении в запросе

  • Пишут Окр и Цел в тексте запроса. Их там нет — только ВЫРАЗИТЬ. Окр/Цел оставляйте для модулей.
  • Ждут от ЧИСЛО(15, 0) отбрасывания дроби. Оно округляет. 2,7 станет 3, а не 2.
  • Сравнивают неокруглённые дроби на равенство. После деления у числа длинный хвост, условие = не срабатывает. Приводите обе стороны к одной точности.
  • Маленькая разрядность. ЧИСЛО(6, 2) на крупной сумме — ошибка переполнения. Берите 15 знаков под деньги.
  • Забывают про NULL. После соединения пустое поле «съедает» всю строку. Оборачивайте в ЕСТЬNULL до округления.
  • Округляют итог там, где нужен построчный расчёт. Копеечные расхождения между суммой строк и итогом документа — почти всегда отсюда.

Частые вопросы

Почему `Окр()` не работает в запросе?
Потому что это функция встроенного языка (модулей), а в языке запросов другой набор функций. Для округления в запросе используйте ВЫРАЗИТЬ(Выражение КАК ЧИСЛО(разрядность, точность)).
Как в запросе округлить до целого в большую сторону?
Штатной функции «округление вверх» в запросе нет. Если нужно гарантированно вверх, прибавьте перед округлением дробную поправку либо выполните расчёт во встроенном языке, где доступен Окр с режимом.
Как округлить до десятков или сотен?
В модуле — Окр(Число, -1) для десятков, Окр(Число, -2) для сотен (отрицательная разрядность). В запросе ВЫРАЗИТЬ отрицательную точность не принимает, поэтому такое округление делают во встроенном языке.
Чем `НАЧАЛОПЕРИОДА` отличается от округления числа?
НАЧАЛОПЕРИОДА работает с датами: приводит дату к началу дня, месяца, квартала или года. К числам она неприменима — для чисел только ВЫРАЗИТЬ.
Почему сумма по строкам не сходится с итогом?
Из-за разного порядка округления. Если строки округляются до копеек по отдельности, их сумма может отличаться от округлённого общего итога на копейку. Для первички округляйте построчно, затем складывайте.