Тест показателей карточки клиента в портале отдела продаж клал время сделки
московской строкой без пояса, а столбцы времени — timestamptz, и сеанс базы
живёт в UTC. Строка «02.08 23:05» читалась как 23:05 UTC, то есть как
03.08 02:05 по Москве — уже за верхней границей периода, которая равна
03.08 00:00 МСК, то есть 02.08 21:00 UTC. Отсюда ноль сделок вместо одной.
Днём эта ошибка не видна вовсе: тест зелёный с утра до девяти вечера и
красный после. Утренний прогон 02.08 был честно зелёным — просто было утро.
Ровно этот класс чинился 02.08 в самом отчёте (App\Support\MskBoundary), но
в приборе, который его проверяет, беда осталась. Здесь чиним прибор, а не
показания: мгновение кладётся в UTC.
Приёмка вырезанием: до правки тест падал и в полном наборе, и в одиночку;
после — 8 из 8. Полный набор целиком: 4533 теста, 4529 зелёных, 4 пропущено,
ни одного падения.
Та же схема замечена ещё в нескольких тестах отдела продаж, но сейчас они
зелёные — значит период у них устроен иначе. Вслепую чинить то, чего не
видел красным, не стал; список передан следующей смене.
Решение заказчика 03.07.2026: два вида тарифа вместо трёх — суточный
оклад daily_salary и процент от пополнений topup_step. Убраны
percent_oborot и fix_per_client.
Миграция 2026_07_03_120000 меняет CHECK sales_tariffs.kind, освобождает
FK sales_users.current_tariff_id и sales_client_assignments.tariff_id,
удаляет тарифы уходящих видов. SalesEarningsService для уходящих видов
возвращает 0 по default-ветке. Снимки привязок не трогаются.
Обновлены контроллеры тарифов и дохода, сервисы Earnings и Metrics,
фронт SalesTariffsView и api/sales.ts, демо-сид, тесты бэка и фронта,
CHANGELOG схемы v8.61 и спека портала.
Не на проде: ветка feat/sales-portal-demo.
Проверка: Sales Feature 169/169, Vitest SalesTariffs 10/10, Larastan 0.
Co-Authored-By: Claude Opus 4.8 1M context <noreply@anthropic.com>
Task 3.3a — SalesClientsController наполняет earned_rub через
SalesEarningsService (был null-заглушкой):
- index: доход по каждому клиенту за период по снимку тарифа привязки;
клиент без привязки (начальник видит незакреплённых) → null.
- show (kpi): доход по одному клиенту.
- overview (totals): сумма комиссии по всем привязкам в scope (без оклада).
Тесты Фазы 1 обновлены под реальные значения (percent_oborot 10/20% от
засеянного оборота → 500/5/10000 ₽; привязка без тарифа → 0.0; без
привязки → null). Попутно исправлен ключ params тест-тарифа
(percent→rate, движок читает rate). Sales-набор 132/132, Larastan 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task 1.4a: GET /api/sales/clients/{tenantId} — профиль, KPI (баланс/запас/проекты/лиды-цель/средняя цена лида, earned=null до Фазы 3), проекты, лиды по дням, последние лиды с МАСКИРОВАННЫМ телефоном, активность. 403 для чужого клиента (ScopesSalesOwnership), начальник видит всех. Тест 8/8, весь sales 65/65, stan 0. Один эскейп на сессию.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>