Files
portal/app/tests/Feature/Sales/SalesPeriodRequestTest.php
T
Дмитрий e9edb206c8 @
feat(sales): кабинет начальника — единый период (без 500) + пополнения баланса

Экраны начальника отстали от вчерашних правок кабинета менеджера (§16–§21).

1. Живой баг: «Произвольный» период до выбора дат ронял запрос в 500 на пяти
   точках из шести (сводка отдела, доход, результативность, выплаты, тарифы).
   Разбор периода вынесен в трейт ResolvesSalesPeriod: 422 вместо падения,
   период по умолчанию d30 вместо this — как показывает сам PeriodPicker.

2. Пополнения баланса (topup_rub) добавлены в dashboard/overview (kpi +
   строки менеджеров) и managers/performance. На экранах: плашка «Пополнили
   баланс» в сводке отдела и колонка «Пополнили ₽» в обеих таблицах
   результативности. Из подписей убрано «(мес)» — период больше не месяц.

3. «Воронка отдела» правок не потребовала: она рендерит те же доску и карточку,
   что экран менеджера, а карточка сама грузит журнал и сохраняет контакты.

Тесты: SalesPeriodRequestTest проходит по всем шести точкам сразу.
Pest 263/263, Vitest 1420/1420, vue-tsc чист.
Спека — §22.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
2026-07-18 15:20:44 +03:00

100 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Models\SalesUser;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* TDD: единый разбор периода на ВСЕХ экранах портала продаж.
*
* Владелец пожаловался «выборка по срокам не работает»: выбрав «Произвольный»,
* менеджер отправлял period=custom БЕЗ дат, резолвер бросал InvalidArgumentException,
* и экран получал 500. На «Моих клиентах» это уже починено (422), а экраны
* начальника (сводка отдела, результативность, тарифы, выплаты) и «Мой доход»
* продолжали падать.
*
* Здесь фиксируем инвариант для всех точек сразу:
* 1. period=custom без дат → 422 с понятным текстом, а не 500.
* 2. from позже to → 422.
* 3. Новые периоды (today/yesterday/d7/d30) принимаются везде.
* 4. Период по умолчанию — d30 (как в PeriodPicker), а не месяц.
*
* Изоляция: DatabaseTransactions. DB_DATABASE=liderra_testing обязателен.
*/
uses(DatabaseTransactions::class);
function per_head(): SalesUser
{
return SalesUser::create([
'name' => 'Начальник '.uniqid(),
'email' => 'perhead'.uniqid().'@sales.local',
'password' => bcrypt('secret'),
'role' => 'head',
'is_active' => true,
]);
}
/**
* Все GET-точки, которые принимают период. Начальник видит все,
* поэтому одним пользователем покрываем и менеджерские, и boss-only.
*
* @return list<string>
*/
function per_endpoints(): array
{
return [
'/api/sales/clients',
'/api/sales/dashboard/overview',
'/api/sales/income',
'/api/sales/managers/performance',
'/api/sales/payouts/remaining',
'/api/sales/tariffs',
];
}
it('произвольный период без дат отвечает 422, а не падает', function () {
$head = per_head();
foreach (per_endpoints() as $url) {
$this->actingAs($head, 'sales')
->getJson($url.'?period=custom')
->assertStatus(422)
->assertJsonValidationErrors('period');
}
});
it('произвольный период с одной датой отвечает 422', function () {
$head = per_head();
foreach (per_endpoints() as $url) {
$this->actingAs($head, 'sales')
->getJson($url.'?period=custom&from=2026-07-01')
->assertStatus(422)
->assertJsonValidationErrors('period');
}
});
it('перевёрнутый произвольный период отвечает 422', function () {
$head = per_head();
foreach (per_endpoints() as $url) {
$this->actingAs($head, 'sales')
->getJson($url.'?period=custom&from=2026-07-10&to=2026-07-01')
->assertStatus(422)
->assertJsonValidationErrors('period');
}
});
it('новые периоды календаря принимаются на всех экранах', function () {
$head = per_head();
foreach (per_endpoints() as $url) {
foreach (['today', 'yesterday', 'd7', 'd30'] as $kind) {
$this->actingAs($head, 'sales')
->getJson($url.'?period='.$kind)
->assertOk();
}
}
});