План разработки: статистика МОП (менеджер отдела продаж)
Документ для поэтапной реализации. По мере выполнения отмечайте пункты:
[ ]→[x].
Последнее обновление: 2026-08-29
Связанный документ: STATISTICS_MODULE_PLAN.md (часть 1 — каналы трафика)
1. Цель и scope
Модуль учёта эффективности менеджеров отдела продаж (МОП): ежедневный ввод фактических показателей менеджером, постановка месячных планов директором, дашборд с разрезами по дням / неделям / месяцу (% выполнения = факт / план).
Принцип работы тот же, что у статистики каналов (SMM / подрядчик):
- обязательная модалка при входе за незаполненные дни;
- очередь pending с самого старого пропущенного дня до вчера;
- равномерное распределение месячного плана по дням;
- директор задаёт план, сотрудник заполняет факт.
Отличие от каналов
| Аспект | Каналы трафика | МОП |
|---|---|---|
| Сущность | traffic_channel + pivot пользователей | Пользователь с ролью mop |
| Отчёт | Один на канал в день | Один на менеджера в день |
| Поля | Охват, визиты, заявки… | Воронка продаж + доп. продажи (ОСАГО / КАСКО) |
| Permission заполнения | fill_statistics_daily | fill_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 | Несколько МОП | Каждый менеджер — отдельная строка плана и отдельный ежедневный отчёт |
| 12 | MOP видит чужие данные | Только своя статистика (view_statistics_mop_own); без каналов и раздела «По сотрудникам» |
| 13 | Праздники РФ | Не учитываем в v1 (только сб/вс). Календарь праздников — отдельный этап при необходимости |
3. Роли и права доступа
3.1. Роль
| Slug | Описание | Статус |
|---|---|---|
mop | МОП (менеджер отдела продаж) | Уже есть — расширить permissions |
3.2. Новые permissions (module: 'statistics')
| Permission | Роли | Назначение |
|---|---|---|
fill_statistics_mop_daily | mop | Заполнение ежедневного отчёта МОП |
view_statistics_mop_own | mop | Просмотр только своей статистики МОП |
view_statistics_mop_all | director, administrator, superadmin | Все менеджеры МОП |
manage_statistics_mop_plans | director, superadmin | Месячные планы по МОП |
Существующие
view_statistics,view_statistics_allдля каналов не выдаются МОП. У МОП только права блока МОП +view_statisticsдля пункта меню.
Миграция: расширить backend/modules/Statistics/database/migrations/ или новая миграция ..._add_mop_statistics_permissions.php.
4. Метрики
4.1. Блок «Продажи»
Заполняемые вручную (факт и план)
| Поле (UI) | Ключ API | Тип |
|---|---|---|
| Количество заявок в AmoCRM | amo_applications | integer ≥ 0 |
| Кол-во взятых в работу | taken_into_work | integer ≥ 0 |
| Кол-во дозвонился | reached | integer ≥ 0 |
| Кол-во квал. заявок | qualified_applications | integer ≥ 0 |
| Кол-во целевых | target_applications | integer ≥ 0 |
| Количество договоров | contracts | integer ≥ 0 |
| Кол-во предоплат | prepayments | integer ≥ 0 |
| Поступило в кассу, руб | cash_revenue | decimal ≥ 0 |
Вычисляемые (не хранить в БД)
| Метрика (UI) | Ключ API | Формула | При делении на 0 |
|---|---|---|---|
| Конверсия в дозвонился | conversion_reached | reached / taken_into_work | null |
| Конверсия в квал. заявку | conversion_qualified | qualified_applications / reached | null |
| Конверсия в целевой | conversion_target | target_applications / qualified_applications | null |
| Конверсия в договор | conversion_contract | contracts / target_applications | null |
| Конверсия в предоплату | conversion_prepayment | prepayments / contracts | null |
4.2. Блок «Доп. продажи»
Сначала вводятся расчёты и договоры по обоим продуктам, затем суммы премий; конверсии и прогноз КВ считаются автоматически.
Заполняемые вручную (факт и план)
| Поле (UI) | Ключ API | Тип |
|---|---|---|
| Кол-во расчётов ОСАГО | osago_calculations | integer ≥ 0 |
| Кол-во расчётов КАСКО | kasko_calculations | integer ≥ 0 |
| Кол-во заключённых договоров ОСАГО | osago_contracts | integer ≥ 0 |
| Кол-во заключённых договоров КАСКО | kasko_contracts | integer ≥ 0 |
| Сумма страховых премий ОСАГО | osago_premium_sum | decimal ≥ 0 |
| Сумма страховых премий КАСКО | kasko_premium_sum | decimal ≥ 0 |
Вычисляемые (не хранить в БД)
| Метрика (UI) | Ключ API | Формула | При делении на 0 |
|---|---|---|---|
| Конверсия в оформление ОСАГО | conversion_osago | osago_contracts / osago_calculations | null |
| Конверсия в оформление КАСКО | conversion_kasko | kasko_contracts / kasko_calculations | null |
| Прогноз КВ ОСАГО (25%) | osago_kv_forecast | osago_premium_sum × 25% | 0 если премия 0 |
| Прогноз КВ КАСКО (30%) | kasko_kv_forecast | kasko_premium_sum × 30% | 0 если премия 0 |
Премии и прогноз КВ планируются директором отдельно (ввод сумм премий в плане); КВ в плане считается по той же формуле от плановой суммы премии.
Статус v1: блок «Доп. продажи» (ОСАГО/КАСКО) временно скрыт в UI и принудительно сохраняется как 0 (MopStatisticsMetricsService::ADDITIONAL_SALES_ENABLED = false). Поля в БД остаются.
Вычисление — в MopStatisticsMetricsService (backend); для таблицы UI — useMopStatistics.ts / константа порядка строк.
4.3. Порядок строк в таблице дашборда
Чередование в блоке Продажи: «базовая метрика → конверсия → следующий шаг воронки» (как в каналах).
В блоке Доп. продажи: сначала оба продукта на одном шаге (расчёты → конверсии → договоры → премии → КВ).
Продажи
- Количество заявок в AmoCRM
- Кол-во взятых в работу
- Конверсия в дозвонился
- Кол-во дозвонился
- Конверсия в квал. заявку
- Кол-во квал. заявок
- Конверсия в целевой
- Кол-во целевых
- Конверсия в договор
- Количество договоров
- Конверсия в предоплату
- Кол-во предоплат
- Поступило в кассу, руб
Доп. продажи (подзаголовок в таблице; порядок — сначала оба продукта на шаге, затем следующий шаг)
- Кол-во расчётов ОСАГО
- Кол-во расчётов КАСКО
- Конверсия в оформление ОСАГО
- Конверсия в оформление КАСКО
- Кол-во заключённых договоров ОСАГО
- Кол-во заключённых договоров КАСКО
- Сумма страховых премий ОСАГО
- Сумма страховых премий КАСКО
- Прогноз КВ ОСАГО (25%)
- Прогноз КВ КАСКО (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записи роли в pivotrole_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Переиспользовать по образцу:
StatisticsPendingReportService→MopStatisticsPendingReportServiceStatisticsPlanDistributionService→MopStatisticsPlanDistributionService(или обобщить базовый trait)StatisticsSummaryService→MopStatisticsSummaryService
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. Справочник МОП
| Метод | URL | Permission | Описание |
|---|---|---|---|
| GET | /mop/managers | view_statistics_mop_own / view_statistics_mop_all | Список МОП (own → только себя) |
7.2. Ежедневные отчёты
| Метод | URL | Permission | Описание |
|---|---|---|---|
| GET | /mop/pending | fill_statistics_mop_daily | Очередь незаполненных дней |
| POST | /mop/daily-reports | fill_statistics_mop_daily | Отправка факта |
| GET | /mop/daily-reports | view_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. Планы
| Метод | URL | Permission | Описание |
|---|---|---|---|
| GET | /mop/plans?year=&month=&user_id= | view_statistics_mop_all | Планы за месяц |
| POST | /mop/plans | manage_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:
- Проверить permission
fill_statistics_mop_daily - Определить
start_date— дата начала учёта МОП (users_data.mop_statistics_start_dateили дата назначения роли) - Для каждого рабочего дня (пн–пт) от
start_dateдо вчера найти отсутствующие отчёты - Суббота и воскресенье пропускать — не попадают в очередь и не требуют submit
- Вернуть очередь
[{ 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/mop | director, admin, superadmin, mop |
| Планы МОП | /dashboard/statistics/mop/plans | director, 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— все формулы, деление на 0MopStatisticsPendingReportServiceTest— очередь, start_date, пропуск сб/всStatisticsWorkingDaysServiceTest— подсчёт рабочих дней (общий с каналами)MopStatisticsPlanDistributionServiceTest— дневной план только пн–пт, нулевой план в выходныеMopStatisticsSummaryServiceTest— fact / plan / completion_pct- Feature: POST daily-report, POST plan, 403 без permission
Ручное QA
- Создать пользователя с ролью
mop, задатьmop_statistics_start_date - Войти — модалка за вчера
- Пропустить пятницу — в понедельник модалка за пятницу; сб/вс в очереди нет
- Директор задаёт план на месяц — дневной план = месячный / число рабочих дней
- Проверить недельный разрез: план недели = сумма пн–пт, сб/вс не увеличивают план
13. Связанные файлы
| Назначение | Путь |
|---|---|
| Модуль статистики | backend/modules/Statistics/ |
| План каналов | docs/STATISTICS_MODULE_PLAN.md |
| Subnav | frontend/components/statistics/StatisticsSubNav.vue |
| Модалка каналов (образец) | frontend/components/statistics/StatisticsDailyReportModal.vue |
| Планы каналов (образец) | frontend/pages/dashboard/statistics/plans.vue |
| Права UI | frontend/utils/statisticsAccess.js |
| Permissions migration (образец) | backend/modules/Statistics/database/migrations/2026_08_27_100400_add_statistics_permissions.php |
14. Порядок коммитов
- Миграции + models + permissions + metrics service
- Pending + daily reports API + модалка
- Monthly plans API + UI планов
- Summary API + дашборд МОП + subnav
- Полировка, тесты, документация