Files
portal/app/tests/Feature/Sales/ProspectWarmingBadgeTest.php
T

120 lines
4.6 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Models\SalesAdAudienceFirm;
use App\Models\SalesAdAudienceWarmingEpisode;
use App\Models\SalesProspect;
use App\Models\SalesUser;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\Concerns\SharesSupplierPdo;
/**
* Витрина (кусок B, B4): карточка проспекта в GET /api/sales/prospects несёт блок
* `warming`, считанный из ЛЕТОПИСИ эпизодов (sales_ad_audience_warming_episodes):
*
* warming.active — есть ли хоть один открытый (идущий) эпизод
* warming.channels[code].live — открытый эпизод канала = «идёт сейчас»
* warming.channels[code].count — число эпизодов канала = «×N» (грели N раз)
*
* Фирма (pgsql_supplier) связывается с проспектом (default) через prospect_id —
* сопоставление в PHP (warmingByProspect). SharesSupplierPdo обязателен.
*
* План: docs/superpowers/plans/2026-07-23-progrev-vitrina-kusok-B.md Task B4.
*/
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
function pwb_manager(): SalesUser
{
return SalesUser::create([
'name' => 'Менеджер '.uniqid(),
'email' => 'pwb'.uniqid().'@sales.local',
'password' => bcrypt('secret'),
'role' => 'manager',
'is_active' => true,
]);
}
/** @return array{0: SalesProspect, 1: SalesAdAudienceFirm} */
function pwb_firm(SalesUser $manager, string $name): array
{
$prospect = SalesProspect::create([
'sales_user_id' => $manager->id,
'stage' => 'new',
'source' => 'search',
'firm_name' => $name,
]);
$firm = SalesAdAudienceFirm::create([
'firm_name' => $name,
'warmup_started_at' => now()->subDays(2),
'prospect_id' => $prospect->id,
'assigned_at' => now(),
]);
return [$prospect, $firm];
}
it('открытый эпизод канала → значок «идёт сейчас» (live=true, active=true)', function () {
$manager = pwb_manager();
[, $firm] = pwb_firm($manager, 'Гарант');
foreach (['yandex', 'vk'] as $ch) {
SalesAdAudienceWarmingEpisode::create([
'firm_id' => $firm->id, 'channel' => $ch, 'started_at' => now()->subDays(2),
]);
}
$row = $this->actingAs($manager, 'sales')
->getJson('/api/sales/prospects')->assertOk()->json('prospects.0');
expect($row['warming']['active'])->toBeTrue()
->and($row['warming']['channels']['yandex']['live'])->toBeTrue()
->and($row['warming']['channels']['yandex']['count'])->toBe(1)
->and($row['warming']['channels']['vk']['live'])->toBeTrue();
});
it('только закрытый эпизод → «грели раньше» (live=false, active=false, count сохраняется)', function () {
$manager = pwb_manager();
[, $firm] = pwb_firm($manager, 'Былое');
SalesAdAudienceWarmingEpisode::create([
'firm_id' => $firm->id, 'channel' => 'yandex',
'started_at' => now()->subDays(10), 'ended_at' => now()->subDays(3),
]);
$row = $this->actingAs($manager, 'sales')
->getJson('/api/sales/prospects')->assertOk()->json('prospects.0');
expect($row['warming']['active'])->toBeFalse()
->and($row['warming']['channels']['yandex']['live'])->toBeFalse()
->and($row['warming']['channels']['yandex']['count'])->toBe(1);
});
it('два эпизода СМС → count=2, live=false (СМС ×2, разовая акция)', function () {
$manager = pwb_manager();
[, $firm] = pwb_firm($manager, 'Новодент');
foreach ([now()->subDays(5), now()->subDay()] as $t) {
SalesAdAudienceWarmingEpisode::create([
'firm_id' => $firm->id, 'channel' => 'sms', 'started_at' => $t, 'ended_at' => $t,
]);
}
$row = $this->actingAs($manager, 'sales')
->getJson('/api/sales/prospects')->assertOk()->json('prospects.0');
expect($row['warming']['channels']['sms']['count'])->toBe(2)
->and($row['warming']['channels']['sms']['live'])->toBeFalse();
});
it('нет эпизодов → пустой прогрев (active=false, каналов нет)', function () {
$manager = pwb_manager();
pwb_firm($manager, 'Пусто');
$row = $this->actingAs($manager, 'sales')
->getJson('/api/sales/prospects')->assertOk()->json('prospects.0');
expect($row['warming']['active'])->toBeFalse()
->and($row['warming']['channels'])->toBe([]);
});