Skip to content

План разработки: статистика МОП (менеджер отдела продаж)

Документ для поэтапной реализации. По мере выполнения отмечайте пункты: [ ][x].
Последнее обновление: 2026-08-29
Связанный документ: STATISTICS_MODULE_PLAN.md (часть 1 — каналы трафика)


1. Цель и scope

Модуль учёта эффективности менеджеров отдела продаж (МОП): ежедневный ввод фактических показателей менеджером, постановка месячных планов директором, дашборд с разрезами по дням / неделям / месяцу (% выполнения = факт / план).

Принцип работы тот же, что у статистики каналов (SMM / подрядчик):

  • обязательная модалка при входе за незаполненные дни;
  • очередь pending с самого старого пропущенного дня до вчера;
  • равномерное распределение месячного плана по дням;
  • директор задаёт план, сотрудник заполняет факт.

Отличие от каналов

АспектКаналы трафикаМОП
Сущностьtraffic_channel + pivot пользователейПользователь с ролью mop
ОтчётОдин на канал в деньОдин на менеджера в день
ПоляОхват, визиты, заявки…Воронка продаж + доп. продажи (ОСАГО / КАСКО)
Permission заполненияfill_statistics_dailyfill_statistics_mop_daily

Входит в scope

  • Роль mop уже создана — добавить permissions и UI
  • Ежедневные отчёты МОП (модалка)
  • Месячные планы по каждому МОП (директор)
  • Дашборд: день / неделя / месяц, фильтр по менеджеру
  • Вкладка в разделе «Статистика» (subnav)

Не входит (следующие этапы)

  • Автоподтягивание заявок из AmoCRM
  • Автоподтягивание кассы из учётной системы
  • Экспорт Excel / PDF, Telegram-напоминания

2. Согласованные решения

#ВопросРешение
1Привязка отчётаК user_id менеджера с ролью mop, без канала трафика
2Кто заполняетТолько роль mop (permission fill_statistics_mop_daily)
3Кто ставит планДиректор / superadmin на каждого МОП отдельно
4Пропуск дня (отпуск, болезнь)Модалка при следующем входе за последний незаполненный рабочий день
5Старт очереди pendingС даты начала учёта МОП (users_data.mop_statistics_start_date); только рабочие дни
6Выходные (сб, вс)Не требуют отчёта и не входят в план (вариант C)
7Распределение планадневной план = месячный / кол-во рабочих дней в месяце; в сб/вс план = 0
8Конверсии в планеСчитаются из плановых базовых полей теми же формулами, что и для факта
9Воронка продажAmoCRM → в работу → дозвонился → квал → целевой → договор → предоплата → касса (см. §4.1)
10Прогноз КВosago_premium_sum × 25%, kasko_premium_sum × 30% — вычисляемые, в план не вводятся отдельно
11Несколько МОПКаждый менеджер — отдельная строка плана и отдельный ежедневный отчёт
12MOP видит чужие данныеТолько своя статистика (view_statistics_mop_own); без каналов и раздела «По сотрудникам»
13Праздники РФНе учитываем в v1 (только сб/вс). Календарь праздников — отдельный этап при необходимости

3. Роли и права доступа

3.1. Роль

SlugОписаниеСтатус
mopМОП (менеджер отдела продаж)Уже есть — расширить permissions

3.2. Новые permissions (module: 'statistics')

PermissionРолиНазначение
fill_statistics_mop_dailymopЗаполнение ежедневного отчёта МОП
view_statistics_mop_ownmopПросмотр только своей статистики МОП
view_statistics_mop_alldirector, administrator, superadminВсе менеджеры МОП
manage_statistics_mop_plansdirector, superadminМесячные планы по МОП

Существующие view_statistics, view_statistics_all для каналов не выдаются МОП. У МОП только права блока МОП + view_statistics для пункта меню.

Миграция: расширить backend/modules/Statistics/database/migrations/ или новая миграция ..._add_mop_statistics_permissions.php.


4. Метрики

4.1. Блок «Продажи»

Заполняемые вручную (факт и план)

Поле (UI)Ключ APIТип
Количество заявок в AmoCRMamo_applicationsinteger ≥ 0
Кол-во взятых в работуtaken_into_workinteger ≥ 0
Кол-во дозвонилсяreachedinteger ≥ 0
Кол-во квал. заявокqualified_applicationsinteger ≥ 0
Кол-во целевыхtarget_applicationsinteger ≥ 0
Количество договоровcontractsinteger ≥ 0
Кол-во предоплатprepaymentsinteger ≥ 0
Поступило в кассу, рубcash_revenuedecimal ≥ 0

Вычисляемые (не хранить в БД)

Метрика (UI)Ключ APIФормулаПри делении на 0
Конверсия в дозвонилсяconversion_reachedreached / taken_into_worknull
Конверсия в квал. заявкуconversion_qualifiedqualified_applications / reachednull
Конверсия в целевойconversion_targettarget_applications / qualified_applicationsnull
Конверсия в договорconversion_contractcontracts / target_applicationsnull
Конверсия в предоплатуconversion_prepaymentprepayments / contractsnull

4.2. Блок «Доп. продажи»

Сначала вводятся расчёты и договоры по обоим продуктам, затем суммы премий; конверсии и прогноз КВ считаются автоматически.

Заполняемые вручную (факт и план)

Поле (UI)Ключ APIТип
Кол-во расчётов ОСАГОosago_calculationsinteger ≥ 0
Кол-во расчётов КАСКОkasko_calculationsinteger ≥ 0
Кол-во заключённых договоров ОСАГОosago_contractsinteger ≥ 0
Кол-во заключённых договоров КАСКОkasko_contractsinteger ≥ 0
Сумма страховых премий ОСАГОosago_premium_sumdecimal ≥ 0
Сумма страховых премий КАСКОkasko_premium_sumdecimal ≥ 0

Вычисляемые (не хранить в БД)

Метрика (UI)Ключ APIФормулаПри делении на 0
Конверсия в оформление ОСАГОconversion_osagoosago_contracts / osago_calculationsnull
Конверсия в оформление КАСКОconversion_kaskokasko_contracts / kasko_calculationsnull
Прогноз КВ ОСАГО (25%)osago_kv_forecastosago_premium_sum × 25%0 если премия 0
Прогноз КВ КАСКО (30%)kasko_kv_forecastkasko_premium_sum × 30%0 если премия 0

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

Статус v1: блок «Доп. продажи» (ОСАГО/КАСКО) временно скрыт в UI и принудительно сохраняется как 0 (MopStatisticsMetricsService::ADDITIONAL_SALES_ENABLED = false). Поля в БД остаются.

Вычисление — в MopStatisticsMetricsService (backend); для таблицы UI — useMopStatistics.ts / константа порядка строк.

4.3. Порядок строк в таблице дашборда

Чередование в блоке Продажи: «базовая метрика → конверсия → следующий шаг воронки» (как в каналах).
В блоке Доп. продажи: сначала оба продукта на одном шаге (расчёты → конверсии → договоры → премии → КВ).

Продажи

  1. Количество заявок в AmoCRM
  2. Кол-во взятых в работу
  3. Конверсия в дозвонился
  4. Кол-во дозвонился
  5. Конверсия в квал. заявку
  6. Кол-во квал. заявок
  7. Конверсия в целевой
  8. Кол-во целевых
  9. Конверсия в договор
  10. Количество договоров
  11. Конверсия в предоплату
  12. Кол-во предоплат
  13. Поступило в кассу, руб

Доп. продажи (подзаголовок в таблице; порядок — сначала оба продукта на шаге, затем следующий шаг)

  1. Кол-во расчётов ОСАГО
  2. Кол-во расчётов КАСКО
  3. Конверсия в оформление ОСАГО
  4. Конверсия в оформление КАСКО
  5. Кол-во заключённых договоров ОСАГО
  6. Кол-во заключённых договоров КАСКО
  7. Сумма страховых премий ОСАГО
  8. Сумма страховых премий КАСКО
  9. Прогноз КВ ОСАГО (25%)
  10. Прогноз КВ КАСКО (30%)

5. Модель данных

5.1. ER-схема

mop_statistics_daily_reports
├── id
├── user_id              → users.id (менеджер МОП, чьи показатели)
├── filled_by_user_id    → users.id (кто заполнил; обычно = user_id)
├── report_date          (date)

│   -- Продажи (факт)
├── amo_applications
├── taken_into_work
├── reached
├── qualified_applications
├── target_applications
├── contracts
├── prepayments
├── cash_revenue         (decimal 12,2)

│   -- Доп. продажи (факт)
├── osago_calculations
├── kasko_calculations
├── osago_contracts
├── kasko_contracts
├── osago_premium_sum    (decimal 12,2)
├── kasko_premium_sum    (decimal 12,2)

├── submitted_at         (timestamp)
├── created_at, updated_at
UNIQUE (user_id, report_date)

mop_statistics_monthly_plans
├── id
├── user_id              → users.id (МОП)
├── year                 (smallint)
├── month                (tinyint 1–12)

│   -- План (те же поля, что заполняются вручную)
├── amo_applications_plan
├── taken_into_work_plan
├── reached_plan
├── qualified_applications_plan
├── target_applications_plan
├── contracts_plan
├── prepayments_plan
├── cash_revenue_plan    (decimal 12,2)
├── osago_calculations_plan
├── kasko_calculations_plan
├── osago_contracts_plan
├── kasko_contracts_plan
├── osago_premium_sum_plan (decimal 12,2)
├── kasko_premium_sum_plan (decimal 12,2)

├── created_by           → users.id
├── created_at, updated_at
UNIQUE (user_id, year, month)

mop_role_assignments (опционально, если нет даты в users_data)
├── id
├── user_id              → users.id
├── assigned_at          (date — с какого дня требовать отчёты)
├── created_at, updated_at

Альтернатива без отдельной таблицы: хранить mop_assigned_at в users_data или брать created_at записи роли в pivot role_user. Рекомендация: поле mop_statistics_start_date в users_data (nullable), задаётся при создании / редактировании пользователя с ролью mop.

5.2. Ограничения

  • user_id в отчётах и планах — только пользователи с ролью mop
  • Один отчёт на менеджера за календарный день
  • Один план на менеджера за календарный месяц
  • Валидация воронки на backend (мягкая): reached ≤ taken_into_work, qualified_applications ≤ reached, target_applications ≤ qualified_applications, contracts ≤ target_applications, prepayments ≤ contractsпредупреждение в UI, не блокировать submit

6. Архитектура

Расширение модуля backend/modules/Statistics/, префикс API: /api/v1/statistics/mop.

6.1. Backend (новые файлы)

Statistics/
├── Http/
│   ├── Controllers/
│   │   ├── MopDailyReportController.php
│   │   ├── MopMonthlyPlanController.php
│   │   └── MopStatisticsSummaryController.php
│   └── Requests/
│       ├── StoreMopDailyReportRequest.php
│       └── StoreMopMonthlyPlanRequest.php
├── Services/
│   ├── MopStatisticsMetricsService.php
│   ├── MopStatisticsPendingReportService.php
│   ├── MopStatisticsPlanDistributionService.php
│   ├── MopStatisticsSummaryService.php
│   └── (переиспользовать) StatisticsWorkingDaysService.php
├── Models/
│   ├── MopStatisticsDailyReport.php
│   └── MopStatisticsMonthlyPlan.php
└── database/migrations/
    ├── ..._create_mop_statistics_daily_reports_table.php
    ├── ..._create_mop_statistics_monthly_plans_table.php
    └── ..._add_mop_statistics_permissions.php

Переиспользовать по образцу:

  • StatisticsPendingReportServiceMopStatisticsPendingReportService
  • StatisticsPlanDistributionServiceMopStatisticsPlanDistributionService (или обобщить базовый trait)
  • StatisticsSummaryServiceMopStatisticsSummaryService

6.2. Frontend (новые файлы)

frontend/
├── pages/dashboard/statistics/
│   ├── mop/
│   │   ├── index.vue           # дашборд МОП
│   │   └── plans.vue           # планы директора по МОП
├── components/statistics/mop/
│   ├── MopDailyReportModal.vue
│   ├── MopPlanForm.vue
│   ├── MopMetricsTable.vue
│   ├── MopStatisticsFilters.vue
│   └── MopStatisticsDashboardBody.vue
└── composables/
    └── useMopStatistics.ts

Обновить:

  • StatisticsSubNav.vue — вкладки «Статистика МОП», «Планы МОП»
  • layouts/dashboard.vue — подключить MopDailyReportModal (рядом с канальной модалкой)
  • statisticsAccess.js — хелперы canFillMopDaily, canManageMopPlans, …
  • Settings → пользователи: дата начала учёта статистики МОП (если выбран вариант с users_data)

7. API

Middleware: ['api', 'auth:sanctum'] + permission:*.

7.1. Справочник МОП

МетодURLPermissionОписание
GET/mop/managersview_statistics_mop_own / view_statistics_mop_allСписок МОП (own → только себя)

7.2. Ежедневные отчёты

МетодURLPermissionОписание
GET/mop/pendingfill_statistics_mop_dailyОчередь незаполненных дней
POST/mop/daily-reportsfill_statistics_mop_dailyОтправка факта
GET/mop/daily-reportsview_statistics_mop_*История с фильтрами

POST /mop/daily-reports — тело запроса:

json
{
  "report_date": "2026-08-28",
  "amo_applications": 15,
  "taken_into_work": 12,
  "reached": 10,
  "qualified_applications": 6,
  "target_applications": 4,
  "contracts": 3,
  "prepayments": 2,
  "cash_revenue": 150000.00,
  "osago_calculations": 5,
  "kasko_calculations": 3,
  "osago_contracts": 2,
  "kasko_contracts": 1,
  "osago_premium_sum": 12000.00,
  "kasko_premium_sum": 45000.00
}

Для МОП user_id берётся из auth()->id(), директор не заполняет за менеджера (кроме будущего backfill — вне scope).

7.3. Планы

МетодURLPermissionОписание
GET/mop/plans?year=&month=&user_id=view_statistics_mop_allПланы за месяц
POST/mop/plansmanage_statistics_mop_plansСоздание / обновление
GET/mop/plans/distribution?year=&month=&user_id=view_statistics_mop_*План по дням и неделям

7.4. Сводная статистика

МетодURLОписание
GET/mop/summary?from=&to=&user_id=&group_by=day|week|monthФакт + план + % выполнения + вычисляемые метрики

Пример фрагмента ответа:

json
{
  "period": { "from": "2026-08-01", "to": "2026-08-28" },
  "group_by": "week",
  "user_id": 42,
  "items": [
    {
      "week": 35,
      "from": "2026-08-25",
      "to": "2026-08-31",
      "metrics": {
        "contracts": { "fact": 12, "plan": 15, "completion_pct": 80.0 },
        "conversion_contract": { "fact": 0.75, "plan": 0.8, "completion_pct": null },
        "prepayments": { "fact": 8, "plan": 10, "completion_pct": 80.0 },
        "conversion_prepayment": { "fact": 0.67, "plan": 0.65, "completion_pct": null },
        "cash_revenue": { "fact": 600000, "plan": 500000, "completion_pct": 120.0 }
      }
    }
  ]
}

Для конверсий completion_pct может быть null (сравнение долей не всегда осмысленно) — отображать в UI «—» или только факт / план без %.


8. Бизнес-логика

8.1. Ежедневная модалка

MopStatisticsPendingReportService:

  1. Проверить permission fill_statistics_mop_daily
  2. Определить start_date — дата начала учёта МОП (users_data.mop_statistics_start_date или дата назначения роли)
  3. Для каждого рабочего дня (пн–пт) от start_date до вчера найти отсутствующие отчёты
  4. Суббота и воскресенье пропускать — не попадают в очередь и не требуют submit
  5. Вернуть очередь [{ reportDate }, ...] (один элемент на день; у МОП нет нескольких «каналов»)

Если start_date попадает на выходной — первый pending = ближайший следующий рабочий день с незаполненным отчётом.

MopDailyReportModal.vue:

  • Подключить в layouts/dashboard.vue
  • При загрузке → GET /statistics/mop/pending
  • v-dialog persistent — нельзя закрыть без успешного submit
  • Форма: дата (readonly), два блока полей (Продажи / Доп. продажи), валидация ≥ 0
  • После POST — следующий элемент очереди или закрытие

Параллельная модалка каналов: если у пользователя есть оба permission (редко) — сначала каналы, потом МОП, или единая очередь с type: 'channel' | 'mop' (рекомендация: отдельные модалки, канальная первой).

8.2. Распределение плана (только рабочие дни)

Отличие от каналов: нет — та же логика рабочих дней (пн–пт), общий StatisticsWorkingDaysService.

MopStatisticsPlanDistributionService + StatisticsWorkingDaysService:

Та же логика, что у каналов (см. STATISTICS_MODULE_PLAN.md §8.2).

working_days_in_month = COUNT(даты месяца, где день недели пн–пт)

daily_plan[metric][date] =
  monthly_plan[metric] / working_days_in_month   // если date — рабочий день
  0                                              // если date — сб или вс

weekly_plan[metric][week] = SUM(daily_plan[metric] for dates in ISO week)

plan_for_period = SUM(daily_plan[metric] for working dates in [from..to])

completion_pct = (SUM(fact) / plan_for_period) * 100

Пример: план 100 заявок на август 2026 (21 рабочий день) → дневной план ≈ 4,76 на каждый пн–пт; в сб/вс план = 0, отчёт не нужен.

Период «текущий месяц» на дашборде обрезается по сегодня; в план периода входят только прошедшие рабочие дни (включая сегодня, если это пн–пт).

API /mop/plans/distribution: в ответе для выходных дней metrics.* = 0, флаг isWorkingDay: false (опционально, для UI).

8.3. Разграничение доступа

РольВидит
mopСвои отчёты и свой план / факт
director, administrator, superadminВсе каналы, все МОП (через view_statistics_mop_all), управление планами
contractor, smm_specialistНет доступа к разделу МОП

9. Frontend — экраны

9.1. Дашборд /dashboard/statistics/mop

  • Фильтры: период (месяц), менеджер (для director), группировка день / неделя / месяц
  • Таблица: План | Факт | % выполнения по метрикам из §4.3
  • Accordion по неделям (переиспользовать StatisticsWeekBreakdown или MOP-вариант)
  • Графики (опционально, этап 4): ключевые метрики воронки + касса
js
definePageMeta({
  layout: 'dashboard',
  middleware: ['auth', 'role'],
  roles: ['director', 'administrator', 'superadmin', 'mop'],
})

9.2. Планы /dashboard/statistics/mop/plans

  • Только director / superadmin
  • Выбор месяца + список МОП
  • Форма плана: все поля из §4.1–4.2 (без вычисляемых)
  • Паттерн: frontend/pages/dashboard/statistics/plans.vue

9.3. Subnav

Добавить в StatisticsSubNav.vue:

ВкладкаURLКто видит
Статистика МОП/dashboard/statistics/mopdirector, admin, superadmin, mop
Планы МОП/dashboard/statistics/mop/plansdirector, superadmin

9.4. Дашборд директора

На /dashboard (director) — карточка-ссылка «Статистика МОП» рядом со статистикой каналов.


10. Этапы реализации

Этап 1 — Основа (1–2 дня)

Этап 2 — Ежедневные отчёты (2–3 дня)

Этап 3 — Месячные планы (1–2 дня)

Этап 4 — Дашборд (2–3 дня)

Этап 5 — Полировка (1–2 дня)

Оценка: ~8–12 рабочих дней


11. Риски и митигация

РискМитигация
Две модалки (канал + МОП) у одного пользователяРедкий кейс; последовательный показ, документировать порядок
Разная логика дней: каналы vs МОПОдинаковая логика через StatisticsWorkingDaysService
Праздники в рабочие дниv1: праздники считаются рабочими для отчёта; при необходимости — production calendar
Несогласованность воронки (reached > taken_into_work)Мягкая валидация + подсказка в UI
Конверсии в % выполнения планаНе показывать % для conversion_* и kv_forecast
AmoCRM вручнуюПоле «заявки AmoCRM» — ручной ввод до интеграции
Дублирование кода с каналамиВынести общий PlanDistributionTrait / базовый summary при рефакторинге

12. Тестирование

PHPUnit

  • MopStatisticsMetricsServiceTest — все формулы, деление на 0
  • MopStatisticsPendingReportServiceTest — очередь, start_date, пропуск сб/вс
  • StatisticsWorkingDaysServiceTest — подсчёт рабочих дней (общий с каналами)
  • MopStatisticsPlanDistributionServiceTest — дневной план только пн–пт, нулевой план в выходные
  • MopStatisticsSummaryServiceTest — fact / plan / completion_pct
  • Feature: POST daily-report, POST plan, 403 без permission

Ручное QA

  1. Создать пользователя с ролью mop, задать mop_statistics_start_date
  2. Войти — модалка за вчера
  3. Пропустить пятницу — в понедельник модалка за пятницу; сб/вс в очереди нет
  4. Директор задаёт план на месяц — дневной план = месячный / число рабочих дней
  5. Проверить недельный разрез: план недели = сумма пн–пт, сб/вс не увеличивают план

13. Связанные файлы

НазначениеПуть
Модуль статистикиbackend/modules/Statistics/
План каналовdocs/STATISTICS_MODULE_PLAN.md
Subnavfrontend/components/statistics/StatisticsSubNav.vue
Модалка каналов (образец)frontend/components/statistics/StatisticsDailyReportModal.vue
Планы каналов (образец)frontend/pages/dashboard/statistics/plans.vue
Права UIfrontend/utils/statisticsAccess.js
Permissions migration (образец)backend/modules/Statistics/database/migrations/2026_08_27_100400_add_statistics_permissions.php

14. Порядок коммитов

  1. Миграции + models + permissions + metrics service
  2. Pending + daily reports API + модалка
  3. Monthly plans API + UI планов
  4. Summary API + дашборд МОП + subnav
  5. Полировка, тесты, документация