65 lines
3.3 KiB
PHP
65 lines
3.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use App\Models\Project;
|
||
|
|
use App\Models\SupplierProject;
|
||
|
|
use App\Models\Tenant;
|
||
|
|
use App\Services\LeadRouter;
|
||
|
|
use Carbon\Carbon;
|
||
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||
|
|
use Tests\Concerns\SharesSupplierPdo;
|
||
|
|
|
||
|
|
uses(DatabaseTransactions::class);
|
||
|
|
uses(SharesSupplierPdo::class);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Шов «общий источник на группу клиентов» (§9b/§9c): один источник кормит несколько
|
||
|
|
* клиентов. Смена источника у клиента A не должна лишать клиента B доставки по общему
|
||
|
|
* источнику. Матч по слепку: снимок B хранит общий источник → B доезжает независимо от A.
|
||
|
|
*/
|
||
|
|
it('смена источника у клиента A не ломает доставку клиенту B на общем источнике', function () {
|
||
|
|
Carbon::setTestNow(Carbon::parse('2026-05-28 12:00:00', 'Europe/Moscow'));
|
||
|
|
|
||
|
|
DB::table('system_settings')->updateOrInsert(
|
||
|
|
['key' => 'routing_match_by_snapshot'],
|
||
|
|
['value' => 'true', 'type' => 'bool', 'updated_at' => now()],
|
||
|
|
);
|
||
|
|
|
||
|
|
// Клиент A — уже сменил источник на LIVE-уровне (NewSender), но снимок A ещё на Caranga.
|
||
|
|
$tenantA = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]);
|
||
|
|
$projectA = Project::factory()->for($tenantA)->create([
|
||
|
|
'is_active' => true, 'signal_type' => 'sms', 'signal_identifier' => null,
|
||
|
|
'sms_senders' => ['NewSender'], 'sms_keyword' => null,
|
||
|
|
'delivery_days_mask' => 127, 'daily_limit_target' => 100, 'delivered_today' => 0, 'regions' => [],
|
||
|
|
]);
|
||
|
|
// Клиент B — не трогал источник, остаётся на общем Caranga.
|
||
|
|
$tenantB = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]);
|
||
|
|
$projectB = Project::factory()->for($tenantB)->create([
|
||
|
|
'is_active' => true, 'signal_type' => 'sms', 'signal_identifier' => null,
|
||
|
|
'sms_senders' => ['Caranga'], 'sms_keyword' => null,
|
||
|
|
'delivery_days_mask' => 127, 'daily_limit_target' => 100, 'delivered_today' => 0, 'regions' => [],
|
||
|
|
]);
|
||
|
|
|
||
|
|
foreach ([$projectA, $projectB] as $p) {
|
||
|
|
DB::table('project_routing_snapshots')->insert([
|
||
|
|
'snapshot_date' => '2026-05-28', 'project_id' => $p->id, 'tenant_id' => $p->tenant_id,
|
||
|
|
'daily_limit' => 10, 'delivery_days_mask' => 127, 'regions' => '{}',
|
||
|
|
'signal_type' => 'sms', 'signal_identifier' => null,
|
||
|
|
'sms_senders' => json_encode(['Caranga']), 'sms_keyword' => null, // оба снимка на общем Caranga
|
||
|
|
'expected_volume' => 10, 'delivered_count' => 0, 'created_at' => now(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$sharedSp = SupplierProject::factory()->create([
|
||
|
|
'platform' => 'B3', 'signal_type' => 'sms', 'unique_key' => 'Caranga',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$matched = app(LeadRouter::class)->matchEligibleProjects($sharedSp);
|
||
|
|
|
||
|
|
// оба клиента (A и B) доезжают по общему источнику — по одному на tenant
|
||
|
|
expect($matched)->toHaveCount(2)
|
||
|
|
->and($matched->pluck('id')->all())->toContain($projectA->id, $projectB->id);
|
||
|
|
Carbon::setTestNow();
|
||
|
|
});
|