Как в СКД рассчитать надбавки водителям на основе условий из регистра накопления и цен из регистра сведений?

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

При разработке сложных отчетов по расчету заработной платы или премий часто возникает ситуация, когда исходные данные (факты поездок) хранятся в одном месте, а стоимостные показатели (прайс-лист) — в другом. Проанализируем типичную задачу: у нас есть Регистр накопления, где зафиксированы поездки водителей с определенными признаками (например, ПоГороду, Перегруз, ПривилегированныйМаршрут), и Регистр сведений, в котором хранятся актуальные цены за каждый из этих видов работ.

Сложность заключается в том, что одна поездка может сочетать в себе несколько условий, каждое из которых добавляет свою сумму к итогу. Простое использование оператора ВЫБОР КОГДА в одном запросе часто приводит к громоздкому и нечитаемому коду, который сложно поддерживать. Рассмотрим подробнее несколько профессиональных способов решения этой задачи в системе компоновки данных (СКД).

Метод 1. Объединение запросов (UNION ALL)

Этот метод, предложенный в обсуждении, считается одним из самых надежных с точки зрения логики. Мы разделяем получение каждой надбавки на отдельный блок запроса, а затем объединяем их в одну выборку. Разберем по шагам, как это работает:

  1. В первом блоке выбираем только поездки «по городу» и присоединяем к ним цену за город.
  2. Во втором блоке выбираем поездки «с перегрузом» и присоединяем цену за перегруз.
  3. В третьем блоке выбираем «привилегированные маршруты» и соответствующую им цену.
  4. Все части объединяем оператором ОБЪЕДИНИТЬ ВСЕ.

Посмотрим на пример структуры такого запроса:


ВЫБРАТЬ
    Поездки.Водитель,
    Поездки.Период,
    Поездки.Регистратор,
    "По городу" КАК ВидНадбавки,
    Цены.Цена КАК Сумма
ИЗ
    РегистрНакопления.РасчетЗарплатыВодителям КАК Поездки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаценкиВодителям.СрезПоследних(&Период, ВидРабот = ЗНАЧЕНИЕ(Перечисление.ВидыРабот.ПоГороду)) КАК Цены
ПО Истина
ГДЕ
    Поездки.МаршрутПоГороду = ИСТИНА

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Поездки.Водитель,
    Поездки.Период,
    Поездки.Регистратор,
    "За перегруз" КАК ВидНадбавки,
    Цены.Цена КАК Сумма
ИЗ
    РегистрНакопления.РасчетЗарплатыВодителям КАК Поездки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаценкиВодителям.СрезПоследних(&Период, ВидРабот = ЗНАЧЕНИЕ(Перечисление.ВидыРабот.Перегруз)) КАК Цены
ПО Истина
ГДЕ
    Поездки.Перегруз = ИСТИНА

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

Метод 2. Связи наборов данных в СКД

Выясним причину, почему разработчики часто выбирают именно связи наборов данных. Если логика определения «среза последних» цен должна зависеть от даты каждой конкретной поездки, а не от общей даты отчета, механизм связей СКД становится незаменимым.

Создадим два набора данных:

Теперь перейдем на вкладку «Связи наборов данных». Укажем НаборДанных1 как источник, а НаборДанных2 как приемник. В колонке «Параметры» свяжем поле ДатаПоездки с параметром Период, а логическое условие (вид работы) с параметром ВидРабот. Таким образом, СКД сама выполнит запрос к ценам для каждой строки поездки, подставив нужную дату и вид наценки.

Метод 3. Использование вычисляемых полей и функций общих модулей

Проанализируем ситуацию, когда алгоритм расчета цены настолько сложен, что его невозможно или крайне затруднительно описать на языке запросов (например, когда цена зависит от стажа водителя, марки машины и региона одновременно). В этом случае нам помогут Вычисляемые поля.

В схеме компоновки данных добавим новое вычисляемое поле ИтоговаяЦена. В выражении этого поля пропишем вызов функции из общего модуля (модуль должен иметь флаг «Сервер» и «Вызов сервера»):

ОбщийМодульРасчетов.РассчитатьСтоимостьПоездки(ДатаПоездки, Водитель, ПараметрыПоездки)

Важный нюанс: Этот метод может работать медленнее на больших объемах данных, так как функция будет вызываться для каждой строки отчета. Однако с точки зрения читаемости кода — это наиболее «чистый» вариант.

Решение проблемы «Среза последних на каждую дату»

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

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

  1. Выбрать все записи из регистра накопления во временную таблицу.
  2. Присоединить к ним таблицу цен, используя условие Цены.Период <= Поездки.Период.
  3. Сгруппировать результат по каждой поездке, выбрав МАКСИМУМ(Цены.Период). Это даст нам дату последней установки цены перед поездкой.
  4. В итоговом запросе еще раз соединиться с регистром сведений по найденной максимальной дате и получить саму Цена.

Архитектурный совет: использование регистров расчета

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

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

Оптимизация производительности

Для ускорения работы отчета, особенно если данных много, придерживайтесь следующих правил:

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

← На главную