Files
portal/app/tests/Feature/AdminAdvertisingSpendTest.php
T

120 lines
4.8 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Models\Tenant;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
uses(DatabaseTransactions::class);
/**
* T4a: SaaS-admin «Реклама: расход и наша маржа» — агрегат по ad_wallet_transactions
* (type=charge, channel=yandex). Наценка глобальная — ad_settings.ad_margin_percent,
* доля НАШЕЙ маржи в клиентской цене; yandex_cost = client_spend × (1 ad_margin_percent/100).
*/
function makeAdCharge(
int $tenantId,
float $amount,
?string $createdAt = null,
string $channel = 'yandex',
string $type = 'charge',
): void {
DB::table('ad_wallet_transactions')->insert([
'tenant_id' => $tenantId,
'type' => $type,
'amount_rub' => $type === 'charge' ? -$amount : $amount,
'balance_rub_after' => '0.00',
'channel' => $channel,
'related_type' => 'campaign',
'related_id' => 1,
'created_at' => $createdAt ?? now(),
]);
}
test('GET /api/admin/advertising/spend считает расход/маржу по тенантам (390/234/156 и 100/60/40)', function () {
$a = Tenant::factory()->create(['organization_name' => 'Окна Москва']);
$b = Tenant::factory()->create(['organization_name' => 'Двери СПб']);
makeAdCharge($a->id, 260.00);
makeAdCharge($a->id, 130.00);
makeAdCharge($b->id, 100.00);
$r = $this->getJson('/api/admin/advertising/spend');
$r->assertStatus(200);
expect($r->json('ad_margin_percent'))->toBe('40.00');
$rowA = collect($r->json('data'))->firstWhere('tenant_id', $a->id);
expect($rowA['tenant_name'])->toBe('Окна Москва');
expect($rowA['client_spend_rub'])->toBe('390.00');
expect($rowA['yandex_cost_rub'])->toBe('234.00');
expect($rowA['our_margin_rub'])->toBe('156.00');
$rowB = collect($r->json('data'))->firstWhere('tenant_id', $b->id);
expect($rowB['client_spend_rub'])->toBe('100.00');
expect($rowB['yandex_cost_rub'])->toBe('60.00');
expect($rowB['our_margin_rub'])->toBe('40.00');
expect($r->json('totals.client_spend_rub'))->toBe('490.00');
expect($r->json('totals.yandex_cost_rub'))->toBe('294.00');
expect($r->json('totals.our_margin_rub'))->toBe('196.00');
});
test('GET /api/admin/advertising/spend сортирует по client_spend_rub desc', function () {
$small = Tenant::factory()->create();
$big = Tenant::factory()->create();
makeAdCharge($small->id, 50.00);
makeAdCharge($big->id, 500.00);
$r = $this->getJson('/api/admin/advertising/spend');
expect($r->json('data.0.tenant_id'))->toBe($big->id);
expect($r->json('data.1.tenant_id'))->toBe($small->id);
});
test('GET /api/admin/advertising/spend игнорирует не-charge и не-yandex строки', function () {
$tenant = Tenant::factory()->create();
makeAdCharge($tenant->id, 200.00); // считается
makeAdCharge($tenant->id, 999.00, null, 'sms'); // другой канал — не считается
makeAdCharge($tenant->id, 999.00, null, 'yandex', 'topup'); // не charge — не считается
$r = $this->getJson('/api/admin/advertising/spend');
$row = collect($r->json('data'))->firstWhere('tenant_id', $tenant->id);
expect($row['client_spend_rub'])->toBe('200.00');
});
test('GET /api/admin/advertising/spend не включает тенанта без расхода', function () {
Tenant::factory()->create(); // без единой ad_wallet_transactions строки
$r = $this->getJson('/api/admin/advertising/spend');
expect($r->json('data'))->toBe([]);
expect($r->json('totals.client_spend_rub'))->toBe('0.00');
});
test('GET /api/admin/advertising/spend period=current_month не включает прошлый месяц, period=all включает', function () {
$tenant = Tenant::factory()->create();
makeAdCharge($tenant->id, 150.00); // текущий месяц
makeAdCharge($tenant->id, 700.00, now()->subMonths(2)->toDateTimeString()); // 2 месяца назад
$current = $this->getJson('/api/admin/advertising/spend?period=current_month');
$row = collect($current->json('data'))->firstWhere('tenant_id', $tenant->id);
expect($row['client_spend_rub'])->toBe('150.00');
$all = $this->getJson('/api/admin/advertising/spend?period=all');
$rowAll = collect($all->json('data'))->firstWhere('tenant_id', $tenant->id);
expect($rowAll['client_spend_rub'])->toBe('850.00');
});
test('GET /api/admin/advertising/spend — гейт: включён + нет REMOTE_USER → 403', function () {
config(['admin.basic_auth_gate' => true]);
config(['admin.basic_auth_allowlist' => ['admin']]);
$this->getJson('/api/admin/advertising/spend')->assertStatus(403);
});