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