40. Manufacture KPI: Веса метрик и тип оплаты «Оклад + KPI»
Date: 2026-03-17
Status
Proposed
Context
Требуется:
Веса KPI — в
adjust_to_desired_salaryуже используетсяgetattr(m, 'weight', 1), но полеweightв моделиKPIMetricотсутствует. Веса задают доли при распределении KPI-суммы по метрикам.Тип оплаты «Оклад + KPI» — новый тип Salary (type=5): фиксированный оклад (80 000 ₽) + переменная KPI-часть (20 000 ₽), распределяемая по сменам по весам метрик (например, 40% план, 40% брак, 20% отчёт). Итоговая ЗП формируется из начислений по каждой смене.
Decision
Часть 1: Веса в KPIMetric
- Добавить в
KPIMetricполеweight(DecimalField, default=1). - При расчёте KPI:
kpi_payment = base_amount * weight. - В
apply_kpi_corrections— обратная формула с учётом weight. - Хранение веса в KPIMetric (разный вес у разных сотрудников для одного типа KPI).
Часть 2: Тип «Оклад + KPI» (type=5)
- Добавить
Salary.type = 5«Оклад + KPI». - Добавить поле
Salary.kpi_monthly_amount(месячный KPI-пул в рублях). - Оклад —
monthly_salary. - Расчёт на смену:
base_per_shift = monthly_salary / work_dayskpi_pool_per_shift = kpi_monthly_amount / shifts_count(смены берутся из фактического графика сотрудника — ActualWorkSchedule со статусамиwork,in_plan; при отсутствии смен — fallback на производственный календарь РФ).- По каждой выполненной метрике:
kpi_payment_i = weight_i * kpi_pool_per_shift
- Ограничение: сумма KPI за месяц не превышает
kpi_monthly_amount(при превышении — пропорциональное уменьшение KPIRecord). - Итоговая ЗП за месяц = сумма
mast_pay_plus_over_time_pay_flatпо сменам (агрегация в отчётах уже есть).
Consequences
- Требуется миграция для KPIMetric.weight и Salary (type, kpi_monthly_amount).
- Расширение
salary_calendar_serviceиactual_work_shedule.calculate_kpi(). - Совместимость: старые KPIMetric получают weight=1, поведение не меняется.
Сценарии использования
Веса KPI (типы 1, 3, 4)
| № | Сценарий | Ожидаемый результат |
|---|---|---|
| 1.1 | Назначение метрик с разным весом | При расчёте смены каждая метрика умножается на свой вес |
| 1.2 | Массовое назначение с весами | POST /kpi-metric/bulk_create с weight |
| 1.3 | Корректировка суммы бригадиром | Обратная формула пересчитывает KPIMetric.value с учётом weight |
| 1.4 | adjust_to_desired_salary | Целевая KPI-сумма распределяется по весам |
| 1.5 | Существующие сотрудники | weight=1 (default) — поведение без изменений |
Тип «Оклад + KPI» (type=5)
| № | Сценарий | Ожидаемый результат |
|---|---|---|
| 2.1 | Перевод на Оклад+KPI | type=5, monthly_salary=80000, kpi_monthly_amount=20000 |
| 2.2 | Все KPI выполнены | base≈3809₽, kpi_pool≈952₽, KPI=952₽, итого≈4761₽/смена |
| 2.3 | Частичное выполнение | KPI = sum(weight_i) * pool только по достигнутым метрикам |
| 2.4 | Формирование ЗП за месяц | Сумма смен = итоговая ЗП (при 21 смене и полном KPI ≈ 100 000₽) |
| 2.5 | Ручные метрики | Бригадир выставляет сумму вручную, вес учитывается при отображении |
Кросс-сценарии
| № | Сценарий | Ожидаемый результат |
|---|---|---|
| 3.1 | Смешанный состав (type=4 и type=5) | Каждый считается по своему типу |
| 3.2 | Deferred-метрики у type=5 | Начисление через bulk_accrue |
Revision (2026-03-17)
Переход KPI type=5 на почасовую модель: kpi_pool_shift = kpi_rate_per_hour × kpi_hours, где kpi_rate_per_hour = kpi_monthly_amount / monthly_norm_hours, kpi_hours = min(actual_hours, shift_duration). При shift=None — kpi_hours = actual_hours. Лимит за месяц — apply_kpi_monthly_cap.