From d19e0d0ffe5e29e024ecf7a40fdb69fd591d16ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sun, 24 May 2026 11:50:58 +0300 Subject: [PATCH] =?UTF-8?q?feat(billing-v2-c):=20BalancePreflightService?= =?UTF-8?q?=20=E2=80=94=20pure-=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BB=D0=B0=D1=82=D1=91=D0=B6=D0=B5=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D0=BE=D0=B1=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task 1.2 Спека C. evaluate(balanceRub, deliveredInMonth, requiredLeads, tiers) → PreflightResult{passes, requiredLeads, capacityLeads, deficitLeads}. Сравнение в лидах через BalanceToLeadsConverter::convert (7 ступеней + месячный объём). 3 unit-теста GREEN. Pint passed. Co-Authored-By: Claude Opus 4.7 --- .../Billing/BalancePreflightService.php | 49 +++++++++++++++++ app/app/Services/Billing/PreflightResult.php | 20 +++++++ .../Billing/BalancePreflightServiceTest.php | 52 +++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 app/app/Services/Billing/BalancePreflightService.php create mode 100644 app/app/Services/Billing/PreflightResult.php create mode 100644 app/tests/Unit/Billing/BalancePreflightServiceTest.php diff --git a/app/app/Services/Billing/BalancePreflightService.php b/app/app/Services/Billing/BalancePreflightService.php new file mode 100644 index 00000000..b56249f1 --- /dev/null +++ b/app/app/Services/Billing/BalancePreflightService.php @@ -0,0 +1,49 @@ + $tiers + */ + public function evaluate( + string $balanceRub, + int $deliveredInMonth, + int $requiredLeads, + Collection $tiers, + ): PreflightResult { + if ($requiredLeads <= 0) { + return new PreflightResult(true, 0, 0, 0); + } + + $capacity = (int) $this->converter->convert($balanceRub, $deliveredInMonth, $tiers)['leads']; + $passes = $capacity >= $requiredLeads; + + return new PreflightResult( + passes: $passes, + requiredLeads: $requiredLeads, + capacityLeads: $capacity, + deficitLeads: $passes ? 0 : ($requiredLeads - $capacity), + ); + } +} diff --git a/app/app/Services/Billing/PreflightResult.php b/app/app/Services/Billing/PreflightResult.php new file mode 100644 index 00000000..eb99899e --- /dev/null +++ b/app/app/Services/Billing/PreflightResult.php @@ -0,0 +1,20 @@ + 1, 'leads_in_tier' => null, 'price_per_lead_kopecks' => 5000, 'is_active' => true]), + ]); +} + +it('passes when balance covers full daily limit', function () { + $service = new BalancePreflightService; + // 2000₽ / 50₽ = 40 лидов capacity; нужно 30 → проходит. + $result = $service->evaluate( + balanceRub: '2000.00', + deliveredInMonth: 0, + requiredLeads: 30, + tiers: makeTiers(), + ); + + expect($result->passes)->toBeTrue(); + expect($result->capacityLeads)->toBe(40); + expect($result->requiredLeads)->toBe(30); + expect($result->deficitLeads)->toBe(0); +}); + +it('fails when balance below full daily limit', function () { + $service = new BalancePreflightService; + // 1000₽ / 50₽ = 20 лидов capacity; нужно 30 → не проходит, дефицит 10. + $result = $service->evaluate( + balanceRub: '1000.00', + deliveredInMonth: 0, + requiredLeads: 30, + tiers: makeTiers(), + ); + + expect($result->passes)->toBeFalse(); + expect($result->capacityLeads)->toBe(20); + expect($result->deficitLeads)->toBe(10); +}); + +it('passes trivially when requiredLeads is zero', function () { + $service = new BalancePreflightService; + $result = $service->evaluate('0.00', 0, 0, makeTiers()); + expect($result->passes)->toBeTrue(); +});