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