При разработке отчетов в системе компоновки данных (СКД) или написании прямых запросов к базе данных «1С:Предприятие» программисты регулярно сталкиваются с необходимостью расчета вычисляемых показателей. Классический пример — вычисление средней цены как результат деления общей суммы на количество товара. Однако на практике на складе могут числиться товары с нулевым количеством (например, из-за суммовых остатков, ошибок учета или особенностей списания).
Если попытаться выполнить стандартное деление в лоб, система выдаст ошибку «Деление на 0», и отчет откажется формироваться либо выведет некорректные данные. В этой статье мы подробно разберем, как правильно реализовать условие проверки деления на ноль как на уровне запроса, так и средствами вычисляемых полей СКД, а также затронем критически важные нюансы расчета итогов и обработки пустых значений.
Если мы хотим решить задачу на самом раннем этапе — при извлечении данных из базы данных СУБД, наиболее эффективным решением будет использование конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ прямо в тексте запроса. Это классический подход, который позволяет получить уже «чистые» и готовые к выводу данные.
Рассмотрим практический пример. Предположим, у нас есть исходный запрос, получающий остатки по счету 41.01:
ВЫБРАТЬ
ХозрасчетныйОстатки.Субконто1 КАК Номенклатура,
СУММА(ХозрасчетныйОстатки.КоличествоОстаток) КАК Количество,
СУММА(ХозрасчетныйОстатки.СуммаОстаток) КАК Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный.Остатки(&Период, Счет = &Счет4101, , ) КАК ХозрасчетныйОстатки
ГРУППИРОВАТЬ ПО
ХозрасчетныйОстатки.Субконто1
Чтобы прямо в этом запросе рассчитать цену и обезопасить себя от деления на ноль, мы преобразуем его следующим образом:
ВЫБРАТЬ
ХозрасчетныйОстатки.Субконто1 КАК Номенклатура,
СУММА(ХозрасчетныйОстатки.КоличествоОстаток) КАК Количество,
СУММА(ХозрасчетныйОстатки.СуммаОстаток) КАК Сумма,
ВЫБОР
КОГДА СУММА(ХозрасчетныйОстатки.КоличествоОстаток) <> 0
ТОГДА СУММА(ХозрасчетныйОстатки.СуммаОстаток) / СУММА(ХозрасчетныйОстатки.КоличествоОстаток)
ИНАЧЕ 0
КОНЕЦ КАК Цена
ИЗ
РегистрБухгалтерии.Хозрасчетный.Остатки(&Период, Счет = &Счет4101, , ) КАК ХозрасчетныйОстатки
ГРУППИРОВАТЬ ПО
ХозрасчетныйОстатки.Субконто1
В данном случае СУБД сначала проверит, равен ли знаменатель нулю. Если количество не равно нулю, будет произведено деление суммы на количество. В противном случае поле Цена примет значение 0. Этот способ работает быстро и надежно на стороне сервера баз данных.
Иногда логику расчета цен нецелесообразно зашивать в сам запрос (например, если мы связываем несколько наборов данных в СКД, и финальное количество рассчитывается уже в компоновщике). В таких ситуациях на помощь приходит вкладка «Вычисляемые поля» в конструкторе схемы компоновки данных.
Для настройки условного расчета выполним следующие шаги:
Цена.
ВЫБОР КОГДА Количество = 0 ТОГДА 0 ИНАЧЕ Сумма / Количество КОНЕЦ
Важное примечание: Обратите внимание, что в выражении вычисляемого поля мы пишем просто 0 после ключевого слова ТОГДА. Распространенной ошибкой новичков является попытка написать Цена = 0, что приводит к синтаксической ошибке «Поле не найдено».
Язык выражений СКД поддерживает более лаконичный аналог конструкции условного выбора — тернарный оператор ?(). Его использование делает формулу более компактной и удобной для чтения.
Если мы хотим применить этот вариант, то на вкладке «Вычисляемые поля» в колонке «Выражение» для поля Цена нам достаточно написать:
?(Количество = 0, 0, Сумма / Количество)
Этот оператор работает точно так же, как аналогичная функция во встроенном языке «1С:Предприятия»: первым параметром передается логическое условие, вторым — значение в случае истины (если количество равно 0, то цена равна 0), третьим — значение в случае лжи.
Просто настроить формулу на вкладке «Вычисляемые поля» недостаточно. Если мы выведем такое поле в отчет и добавим его на вкладку «Ресурсы» с функцией по умолчанию Сумма(Цена), то получим абсолютно неверные данные в строках группировок и в общих итогах. СКД начнет складывать рассчитанные цены детальных строк, что не имеет никакого экономического смысла.
Чтобы средняя цена рассчитывалась корректно для любых уровней группировок (по номенклатуре, складам, контрагентам или по всему отчету в целом), выражение ресурса должно рассчитываться на основе агрегатных функций суммы и количества.
Проанализируем правильную настройку на вкладке «Ресурсы»:
Цена в список ресурсов.
?(Сумма(Количество) = 0, 0, Сумма(Сумма) / Сумма(Количество))
При такой настройке СКД сначала просуммирует количество и сумму по всем детальным записям текущей группировки, проверит, не равен ли нулю суммарный знаменатель, и только после этого произведет деление совокупной суммы на совокупное количество.
Совет: В реальной практике разработчики часто оставляют поле «Выражение» на самой вкладке «Вычисляемые поля» абсолютно пустым, прописывая всю логику расчета только на вкладке «Ресурсы». В этом случае цена будет корректно вычисляться как для детальных строк (где группировка состоит из одной записи), так и для итоговых строк отчета.
При использовании левых соединений в запросах или связей между наборами данных в СКД мы можем столкнуться с тем, что поле Количество или Сумма принимает значение NULL. Любые математические операции со значением NULL (включая деление или сравнение) возвращают NULL. Это может привести к тому, что в отчете вместо красивых нулей будут отображаться пустые ячейки.
Для борьбы с этим поведением мы рекомендуем всегда оборачивать числовые поля в функцию ЕСТЬNULL. Посмотрим, как будет выглядеть наше выражение в вычисляемом поле с учетом этой защиты:
?(ЕСТЬNULL(Количество, 0) = 0, 0, ЕСТЬNULL(Сумма, 0) / Количество)
В данном случае, если поле Количество окажется незаполненным (значение NULL), функция ЕСТЬNULL преобразует его в 0, условие сработает корректно, и результатом выражения станет число 0.
При написании формул непосредственно в выражении вычисляемых полей СКД существует неочевидная синтаксическая особенность платформы «1С:Предприятие». Парсер СКД может выдать трудночитаемую ошибку разбора выражения, если символ косой черты (деления) / написан вплотную к именам полей (например, Сумма/Количество).
Чтобы избежать подобных проблем, всегда отделяйте математический оператор пробелами с обеих сторон: Сумма / Количество.
Если алгоритм расчета цены в будущем планируется усложнить (например, добавить округление до двух знаков, учет различных типов цен, валютную конвертацию или расчет скидок), то писать громоздкие формулы в интерфейсе СКД станет крайне неудобно.
Платформа «1С» позволяет вызывать экспортные функции общих модулей конфигурации прямо из выражений СКД. Для этого общий модуль должен иметь включенные свойства «Клиент», «Сервер» и «Внешнее соединение».
Выясним, как это реализовать на практике. На вкладке «Вычисляемые поля» в колонке «Выражение» мы можем написать простой вызов:
МойОбщийМодуль.РассчитатьЦену(Сумма, Количество)
А в самом общем модуле МойОбщийМодуль мы опишем следующую экспортную функцию с безопасным расчетом:
Функция РассчитатьЦену(Сумма, Количество) Экспорт
Если Количество = Неопределено Или Количество = 0 Тогда
Возврат 0;
КонецЕсли;
Если Сумма = Неопределено Тогда
Возврат 0;
КонецЕсли;
Возврат Округлить(Сумма / Количество, 2);
КонецФункции
Использование функций общих модулей не только упрощает чтение и поддержку схемы компоновки данных, но и позволяет использовать полноценный отладчик для поиска ошибок в расчетах сложных показателей.