pricePerSmsRub(99))->toBe('9.00') ->and($p->pricePerSmsRub(100))->toBe('8.50') ->and($p->pricePerSmsRub(249))->toBe('8.50') ->and($p->pricePerSmsRub(250))->toBe('8.30') ->and($p->pricePerSmsRub(500))->toBe('8.10') ->and($p->pricePerSmsRub(999))->toBe('8.10') ->and($p->pricePerSmsRub(1000))->toBe('8.00') ->and($p->pricePerSmsRub(50000))->toBe('8.00') ->and($p->pricePerSmsRub(0))->toBe('9.00'); // volume floored to >=1 }); it('segments() делегирует калькулятору', function () { $p = app(ClientSmsPricing::class); expect($p->segments('короткий'))->toBe(1); }); /* |-------------------------------------------------------------------------- | Строка листа 3.8: ступень берётся с учётом УЖЕ отправленного за месяц. |-------------------------------------------------------------------------- | Прежде цена считалась по объёму одного заказа: клиент, разбивший месяц на | десять рассылок по сто номеров, платил по самой дорогой ступени, хотя отправил | тысячу. Теперь ступень берётся для «накоплено за месяц + объём этого заказа». | | Помощники с префиксом price* — имена функций в Pest ГЛОБАЛЬНЫЕ. | Синтетические номера 7999… — реальные НИКОГДА. */ /** Уже отправленные в этом месяце СМС (одна строка журнала на $segments штук). */ function priceSentThisMonth(int $tenantId, int $segments): void { ClientSmsMessage::create([ 'tenant_id' => $tenantId, 'phone' => '79990000001', 'status' => ClientSmsMessage::STATUS_SENT, 'segments' => $segments, 'cost_rub' => '0.00', ]); } it('3.8 — ступень берётся для «накоплено за месяц + объём заказа»', function () { $tenant = Tenant::factory()->create(); $p = app(ClientSmsPricing::class); // Заказ сам по себе маленький — 200 СМС, это ступень 8.50. expect($p->pricePerSmsRub(200))->toBe('8.50'); // Но в этом месяце клиент уже отправил 900 — вместе выходит 1 100, ступень 8.00. priceSentThisMonth((int) $tenant->id, 900); expect($p->pricePerSmsRubFor((int) $tenant->id, 200))->toBe('8.00'); }); it('3.8 — контрольный: без накопленного цена та же, что была', function () { // Без этого прогона предыдущий ничего не доказывает: «8.00» могло бы означать // «теперь всегда самая дешёвая ступень» (урок В-54). $tenant = Tenant::factory()->create(); expect(app(ClientSmsPricing::class)->pricePerSmsRubFor((int) $tenant->id, 200))->toBe('8.50'); }); it('3.8 — чужие отправки на мою цену не влияют', function () { $tenant = Tenant::factory()->create(); $other = Tenant::factory()->create(); priceSentThisMonth((int) $other->id, 5000); expect(app(ClientSmsPricing::class)->pricePerSmsRubFor((int) $tenant->id, 200))->toBe('8.50'); }); it('3.8 — предпросмотр отдаёт цену с учётом отправленного за месяц', function () { // Проверка НЕ на сервисе, а через настоящий запрос экрана: иначе вырез «вернуть // старый расчёт в контроллере» прошёл бы незамеченным — сервис-то остался умным. config(['services.sms.sandbox' => true]); $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $this->actingAs($user); priceSentThisMonth((int) $tenant->id, 5000); $response = $this->postJson('/api/sms/preview', [ 'body' => 'Здравствуйте!', 'source' => 'manual', 'phones' => ['79995550001'], ])->assertOk(); expect($response->json('price_rub_per_sms'))->toBe('8.00') ->and($response->json('estimated_cost_rub'))->toBe('8.00'); }); it('3.8 — контрольный: без отправленного предпросмотр даёт прежнюю цену', function () { config(['services.sms.sandbox' => true]); $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id]); $this->actingAs($user); $response = $this->postJson('/api/sms/preview', [ 'body' => 'Здравствуйте!', 'source' => 'manual', 'phones' => ['79995550001'], ])->assertOk(); expect($response->json('price_rub_per_sms'))->toBe('9.00') ->and($response->json('estimated_cost_rub'))->toBe('9.00'); });