fc16500fea
Task 4 плана «три площадки прогрева» кусок B: GET /api/sales/prospects теперь отдаёт warming.active/warming.channels на каждой карточке — сопоставление SalesAdAudienceFirm.prospect_id в PHP (pgsql_supplier против default connection), без cross-connection JOIN. Канал sms — если номер фирмы попал в отправленную (status=sent) рассылку.
113 lines
4.1 KiB
PHP
113 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\SalesAdAudienceFirm;
|
|
use App\Models\SalesAdAudiencePhone;
|
|
use App\Models\SalesProspect;
|
|
use App\Models\SalesUser;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Tests\Concerns\SharesSupplierPdo;
|
|
|
|
/**
|
|
* Task 4 плана «кусок B»: карточка проспекта в ответе GET /api/sales/prospects
|
|
* несёт блок `warming` (активен ли прогрев + список каналов), чтобы канбан
|
|
* воронки мог показать «греется/прогрет». Фирма (pgsql_supplier) связывается
|
|
* с проспектом (default connection) через SalesAdAudienceFirm.prospect_id —
|
|
* JOIN между соединениями невозможен, сопоставление в PHP (SalesProspectController::warmingByProspect).
|
|
*
|
|
* SharesSupplierPdo обязателен — см. AdAudienceWarmingFieldsTest.
|
|
*
|
|
* План: docs/superpowers/plans/2026-07-21-progrev-4-stranicy-portal.md (кусок B, Task 4).
|
|
*/
|
|
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,
|
|
]);
|
|
}
|
|
|
|
it('карточка проспекта несёт состояние прогрева и каналы', function () {
|
|
$manager = pwb_manager();
|
|
$prospect = SalesProspect::create([
|
|
'sales_user_id' => $manager->id,
|
|
'stage' => 'new',
|
|
'source' => 'search',
|
|
'firm_name' => 'Гарант',
|
|
]);
|
|
$firm = SalesAdAudienceFirm::create([
|
|
'firm_inn' => '6678048177',
|
|
'firm_name' => 'Гарант',
|
|
'warmup_started_at' => now()->subDays(2),
|
|
'ch_yandex' => true,
|
|
'ch_vk' => true,
|
|
'prospect_id' => $prospect->id,
|
|
'assigned_at' => now(),
|
|
]);
|
|
SalesAdAudiencePhone::create([
|
|
'phone' => '79000000002',
|
|
'firm_id' => $firm->id,
|
|
'firm_inn' => '6678048177',
|
|
'state' => 'active',
|
|
'expires_at' => now()->addDays(5),
|
|
'added_at' => now(),
|
|
]);
|
|
|
|
$row = $this->actingAs($manager, 'sales')
|
|
->getJson('/api/sales/prospects')
|
|
->assertOk()
|
|
->json('prospects.0');
|
|
|
|
expect($row['warming']['active'])->toBeTrue();
|
|
expect($row['warming']['channels'])->toContain('yandex');
|
|
expect($row['warming']['channels'])->toContain('vk');
|
|
});
|
|
|
|
it('карточка проспекта показывает канал sms, если номер попал в отправленную рассылку', function () {
|
|
$manager = pwb_manager();
|
|
$prospect = SalesProspect::create([
|
|
'sales_user_id' => $manager->id,
|
|
'stage' => 'new',
|
|
'source' => 'search',
|
|
'firm_name' => 'Новодент',
|
|
]);
|
|
$firm = SalesAdAudienceFirm::create([
|
|
'firm_inn' => '6673240656',
|
|
'firm_name' => 'Новодент',
|
|
'warmup_started_at' => now()->subDay(),
|
|
'ch_yandex' => true,
|
|
'prospect_id' => $prospect->id,
|
|
'assigned_at' => now(),
|
|
]);
|
|
SalesAdAudiencePhone::create([
|
|
'phone' => '79220216061',
|
|
'firm_id' => $firm->id,
|
|
'firm_inn' => '6673240656',
|
|
'state' => 'active',
|
|
'expires_at' => now()->addDays(10),
|
|
'added_at' => now(),
|
|
]);
|
|
|
|
$campaignId = DB::connection('pgsql_supplier')->table('sales_sms_campaigns')->insertGetId([
|
|
'title' => 'т', 'body' => 'т', 'status' => 'done', 'created_by' => $manager->id,
|
|
'created_at' => now(), 'updated_at' => now(),
|
|
]);
|
|
DB::connection('pgsql_supplier')->table('sales_sms_messages')->insert([
|
|
'campaign_id' => $campaignId, 'phone' => '79220216061', 'status' => 'sent',
|
|
]);
|
|
|
|
$row = $this->actingAs($manager, 'sales')
|
|
->getJson('/api/sales/prospects')
|
|
->assertOk()
|
|
->json('prospects.0');
|
|
|
|
expect($row['warming']['channels'])->toContain('sms');
|
|
});
|