Почему ВЫРАЗИТЬ в запросе 1С не выводит два знака после запятой и как это исправить?

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

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

Разница между типом данных в базе и его отображением на экране

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

Когда мы пишем в запросе ВЫРАЗИТЬ(Таблица.Цена КАК ЧИСЛО(15, 2)), эта инструкция транслируется на уровень СУБД (например, MS SQL Server, PostgreSQL или IBM DB2) в конструкцию явного приведения типов CAST(... AS NUMERIC(15, 2)). На уровне базы данных поле действительно получает жесткий тип с точностью ровно два знака после запятой. СУБД возвращает платформе 1С числовое значение именно в таком формате.

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

Когда платформа пытается вывести это число на экран, в текстовое поле или в ячейку макета печатной формы, происходит неявное преобразование числа в строку. По умолчанию стандартный алгоритм преобразования типов в 1С отсекает все незначащие нули в дробной части. Платформа делает это из соображений лаконичности интерфейса: число «15.00» превращается в строку «15», а «12.50» превращается в «12,5». Физическое значение в памяти при этом не меняется, меняется лишь его текстовая репрезентация для пользователя.

Почему у некоторых разработчиков этот способ «работал сам по себе»?

Проанализируем интересную дискуссию, возникшую среди участников обсуждения. Некоторые разработчики утверждают, что они всегда использовали ВЫРАЗИТЬ и у них все выводилось корректно без дополнительных настроек. В чем же секрет такого расхождения? Все зависит от того, куда именно и каким образом выводится результат выполненного запроса:

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

Способ 1. Форматирование ячейки непосредственно в макете печатной формы

Это самый простой, чистый и рекомендуемый способ для большинства печатных форм. Нам не нужно усложнять запрос или писать лишний код во встроенном языке. Настройку формата можно выполнить визуально в Конфигураторе. Для изменения макетов напрямую в пользовательском режиме подойдёт инструкция по редактированию печатных форм в программах 1С.

Рассмотрим пошагово, как настроить формат ячейки макета:

  1. Откроем макет нашей печатной формы в конфигураторе.
  2. Выделим ячейку (или шаблон ячейки), в которую выводится параметр (например, ПечЦена).
  3. Откроем свойства этой ячейки (нажатием клавиши F4 или через контекстное меню).
  4. Найдем свойство Формат и нажмем кнопку с тремя точками для открытия конструктора строки формата.
  5. В открывшемся окне перейдем на вкладку «Число» и установим флажок «Дробная часть». В поле количества знаков укажем значение 2. Также можно установить флажок «Выводить нулевые значения» или настроить группировку триад цифр (чтобы большие числа разделялись пробелами, например, «1 500.00»).
  6. Нажмем «ОК». В строке свойства «Формат» появится код, аналогичный ЧДЦ=2.

После этой настройки, даже если в параметре будет передано простое целое число 12, табличный документ принудительно отобразит его в печатной форме как 12,00.

Способ 2. Использование функции Формат() во встроенном языке

Если мы формируем текстовую строку программно, выводим данные в лог, отправляем email или формируем XML/JSON-файлы вручную, нам поможет встроенная функция платформы Формат(). Она принимает два параметра: само преобразуемое значение и специальную строку формата.

Посмотрим на пример кода, демонстрирующий этот подход:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПоступлениеРасходы.НомерСтроки КАК ПечНомПП,
    |	ПоступлениеРасходы.Цена КАК Цена
    |ИЗ
    |	Документ.ПоступлениеУслугПрочихАктивов.Расходы КАК ПоступлениеРасходы";

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    // Неправильно (нули будут отброшены при неявном приведении в строку):
    СтрокаВыводаПростая = "Позиция: " + Выборка.ПечНомПП + ", Цена: " + Выборка.Цена; 
    
    // Правильно (всегда будет выведено ровно 2 знака после запятой):
    СтрокаВыводаФорматная = "Позиция: " + Выборка.ПечНомПП + ", Цена: " + Формат(Выборка.Цена, "ЧДЦ=2");
    
    Сообщить(СтрокаВыводаФорматная);
КонецЦикла;

Параметр ЧДЦ=2 в строке формата расшифровывается как «Число Дробная Часть = 2». Платформа гарантирует, что в результирующей строке всегда будет присутствовать ровно два десятичных знака, независимо от того, целое это число или дробное.

Способ 3. Настройка формата полей в СКД (Системе компоновки данных)

Если мы разрабатываем отчет на базе СКД, лучшей практикой будет задать формат вывода непосредственно в настройках схемы компоновки. Это позволит сохранить гибкость отчета, корректное поведение группировок и правильную типизацию данных для последующего экспорта отчета пользователем в Excel.

Выполним следующие шаги в конструкторе схемы компоновки данных:

  1. Откроем вкладку Наборы данных в конструкторе СКД.
  2. Найдем в списке полей наше числовое поле (например, Цена или Сумма).
  3. В колонке Оформление для нужного поля нажмем на кнопку выбора (три точки).
  4. В открывшемся окне параметров оформления найдем свойство Формат и установим для него флажок.
  5. В появившемся конструкторе формата зададим параметры аналогично первому способу (укажем форматную строку ЧДЦ=2).

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

Важные нюансы округления при использовании ВЫРАЗИТЬ в запросах

Несмотря на то, что оператор ВЫРАЗИТЬ напрямую не решает задачу визуального форматирования, он критически важен для предварительной математической подготовки данных в запросах. Разберем несколько неочевидных тонкостей его работы, о которых должен знать каждый разработчик.

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

Во-вторых, если вам требуется округлить число строго вниз (выполнить усечение дробной части без математического округления в большую сторону), в языке запросов 1С нет готовой функции вроде ЦЕЛ(). В таких случаях программисты используют проверенный математический прием: вычитание половины шага округления перед вызовом функции ВЫРАЗИТЬ.

Посмотрим на примеры таких конструкций в запросе:


ВЫБРАТЬ
    // Округление до целого числа вниз (усечение дробной части):
    ВЫРАЗИТЬ(Таблица.Сумма - 0.5 КАК ЧИСЛО(15, 0)) КАК СуммаБезКопеекВниз,
    
    // Округление до сотых (до копеек) строго в меньшую сторону:
    ВЫРАЗИТЬ(Таблица.Сумма - 0.005 КАК ЧИСЛО(15, 2)) КАК СуммаСКопейкамиВниз
ИЗ
    Справочник.Номенклатура КАК Таблица

В-третьих, не забывайте о накоплении погрешности вычислений в СУБД при работе с дробными числами. При делении больших чисел СУБД может рассчитывать промежуточные данные с очень высокой точностью (до 8 и более знаков после запятой). В некоторых версиях платформы из-за особенностей вещественной арифметики число 125 в результате деления и последующего умножения могло превратиться в 124.99999999. При последующем округлении через ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) мы неожиданно получали 124.99 вместо ожидаемых 125.00.

Чтобы гарантировать точность расчетов и избежать неявных ошибок СУБД, фирма «1С» официально рекомендует применять оператор ВЫРАЗИТЬ ко всем промежуточным операндам (множителям и делителям) в сложных формулах внутри запроса, а не только к финальному результату. Для проверки и отладки сложных выражений в запросах можно использовать инженерную консоль запросов для 1С.

Влияние региональных настроек операционной системы

При неявном преобразовании чисел в строку платформа 1С ориентируется на региональные настройки операционной системы (или настройки сеанса 1С). Например, в качестве разделителя дробной части может использоваться запятая (стандарт для России) или точка (стандарт для США).

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


// Принудительно выводим число по стандартам англоязычной локали (с точкой):
СтрокаАнгл = Формат(Выборка.Цена, "ЧДЦ=2; Л=en_US"); // Результат: "123.45"

// Принудительно выводим число по стандартам русскоязычной локали (с запятой):
СтрокаРус = Формат(Выборка.Цена, "ЧДЦ=2; Л=ru_RU"); // Результат: "123,45"

Резюме лучших практик

Подведем итог нашему подробному анализу проблемы. Для достижения идеального результата придерживайтесь простых правил:

  1. Используйте ВЫРАЗИТЬ в запросах только для математического округления результатов расчетов (например, после деления или распределения сумм), чтобы исключить появление микрокопеек глубоко в дробной части, но не для визуального оформления.
  2. Для печатных форм всегда настраивайте свойство Формат у конкретных ячеек макета в Конфигураторе. Это сохраняет ваш программный код чистым и разделяет логику получения данных от логики их представления.
  3. При работе со строками во встроенном языке всегда оборачивайте числовые переменные в функцию Формат(Значение, "ЧДЦ=2"), если для них критичен вывод дробной части.
← На главную