diff --git a/app/Http/Controllers/Qms/ProController.php b/app/Http/Controllers/Qms/ProController.php index 43a8807..68641fb 100644 --- a/app/Http/Controllers/Qms/ProController.php +++ b/app/Http/Controllers/Qms/ProController.php @@ -67,16 +67,23 @@ class ProController extends Controller ->with('success', 'Your organization already has an active paid plan.'); } + $validated = $request->validate([ + 'branches' => ['required', 'integer', 'min:1', 'max:999'], + ]); + + $branches = $plans->resolveCheckoutBranches($organization, (int) $validated['branches']); + $amountMinor = $plans->priceForBranches('pro', $branches); + return $this->chargeMonthlyWallet( $organization, $billing, - $plans->proPriceMinor($organization), + $amountMinor, 'pro', 'queue_pro', 'queue-pro-'.$organization->id.'-'.now()->format('Y-m-d-His'), - 'Ladill Queue Pro — monthly subscription', + 'Ladill Queue Pro — '.$branches.' branch(es) monthly', 'Welcome to Queue Pro — active for one month.', - $plans->activeBranchCount($organization), + $branches, ); } @@ -97,16 +104,23 @@ class ProController extends Controller ->with('error', "Enterprise billing requires at least {$min} active branches. Add another branch or choose Pro for a single site."); } + $validated = $request->validate([ + 'branches' => ['required', 'integer', 'min:1', 'max:999'], + ]); + + $branches = $plans->resolveCheckoutBranches($organization, (int) $validated['branches']); + $amountMinor = $plans->priceForBranches('enterprise', $branches); + return $this->chargeMonthlyWallet( $organization, $billing, - $plans->enterprisePriceMinor($organization), + $amountMinor, 'enterprise', 'queue_enterprise', 'queue-enterprise-'.$organization->id.'-'.now()->format('Y-m-d-His'), - 'Ladill Queue Enterprise — monthly subscription', + 'Ladill Queue Enterprise — '.$branches.' branch(es) monthly', 'Welcome to Queue Enterprise — active for one month.', - $plans->activeBranchCount($organization), + $branches, ); } @@ -116,6 +130,7 @@ class ProController extends Controller $validated = $request->validate([ 'plan' => ['required', 'in:pro,enterprise'], 'months' => ['required', 'integer', 'in:6,12,24'], + 'branches' => ['required', 'integer', 'min:1', 'max:999'], ]); $organization = $this->organization($request); @@ -134,11 +149,8 @@ class ProController extends Controller ->with('error', "Enterprise billing requires at least {$min} active branches."); } - $monthlyMinor = $plan === 'enterprise' - ? $plans->enterprisePriceMinor($organization) - : $plans->proPriceMinor($organization); - $amountMinor = $monthlyMinor * $months; - $branches = $plans->activeBranchCount($organization); + $branches = $plans->resolveCheckoutBranches($organization, (int) $validated['branches']); + $amountMinor = $plans->priceForBranches($plan, $branches) * $months; $metadata = [ 'organization_id' => $organization->id, diff --git a/app/Services/Qms/PlanService.php b/app/Services/Qms/PlanService.php index 87ec06c..cd4eaab 100644 --- a/app/Services/Qms/PlanService.php +++ b/app/Services/Qms/PlanService.php @@ -84,9 +84,7 @@ class PlanService public function proPriceMinor(Organization $organization): int { - $branches = max(1, $this->activeBranchCount($organization)); - - return $branches * $this->proPricePerBranchMinor(); + return $this->priceForBranches('pro', $this->activeBranchCount($organization)); } public function enterprisePricePerBranchMinor(): int @@ -96,9 +94,26 @@ class PlanService public function enterprisePriceMinor(Organization $organization): int { - $branches = max(1, $this->activeBranchCount($organization)); + return $this->priceForBranches('enterprise', $this->activeBranchCount($organization)); + } - return $branches * $this->enterprisePricePerBranchMinor(); + /** Fixed branch count × per-branch rate (checkout when the user picks seats). */ + public function priceForBranches(string $plan, int $branches): int + { + $branches = max(1, $branches); + $rate = $plan === 'enterprise' + ? $this->enterprisePricePerBranchMinor() + : $this->proPricePerBranchMinor(); + + return $branches * $rate; + } + + public function resolveCheckoutBranches(Organization $organization, int $requested): int + { + $branches = max(1, min(999, $requested)); + $active = max(1, $this->activeBranchCount($organization)); + + return max($branches, $active); } public function canSubscribeEnterprise(Organization $organization): bool diff --git a/resources/views/qms/pro/index.blade.php b/resources/views/qms/pro/index.blade.php index 3e60c7a..7192d8c 100644 --- a/resources/views/qms/pro/index.blade.php +++ b/resources/views/qms/pro/index.blade.php @@ -1,12 +1,30 @@ @php $proPerBranch = number_format($proPricePerBranchMinor / 100, 0); - $proTotal = number_format($proPriceMinor / 100, 0); $enterprisePerBranch = number_format($enterprisePricePerBranchMinor / 100, 0); - $enterpriseTotal = number_format($enterprisePriceMinor / 100, 0); + $initialBranches = max(1, (int) $branchCount); @endphp -
+
@if (session('success'))
{{ session('success') }}
@endif @@ -18,10 +36,10 @@

Choose your Queue plan

Pay monthly from your Ladill wallet, or prepay 6/12/24 months via Paystack. - Pro and Enterprise are billed per active branch. + Pro and Enterprise are billed per branch — set how many branches to include below. Enterprise includes a one-time setup fee — contact sales for onboarding.

-
+
@@ -30,7 +48,20 @@ :class="billing === '{{ $months }}' ? 'bg-indigo-600 text-white' : 'bg-slate-100 text-slate-700 hover:bg-slate-200'" class="rounded-lg px-3 py-1.5 text-sm font-medium transition">{{ $months }} months · Paystack @endforeach + +
+

+ Minimum is your current active branch count ({{ $initialBranches }}). Totals update live for Pro and Enterprise. +

@@ -55,16 +86,12 @@ -

- @if ($branchCount > 0) - {{ $branchCount }} active {{ str('branch')->plural($branchCount) }} = {{ $currency }} {{ $proTotal }}/mo - @else - Per-branch billing - @endif +

+ branch(es) × {{ $currency }} {{ $proPerBranch }}/branch + = {{ $currency }} /mo

  • Advanced routing rules
  • @@ -88,17 +115,18 @@ @elseif ($canManage)
    @csrf - + +
    @else @@ -113,17 +141,13 @@ -

    - @if ($branchCount > 0) - {{ $branchCount }} active {{ str('branch')->plural($branchCount) }} = {{ $currency }} {{ $enterpriseTotal }}/mo - @else - Per-branch billing for multi-site - @endif +

    + branch(es) × {{ $currency }} {{ $enterprisePerBranch }}/branch + = {{ $currency }} /mo

    • Everything in Pro
    • @@ -147,17 +171,18 @@ @if ($canSubscribeEnterprise)
      @csrf - + +
      @else diff --git a/tests/Feature/QmsProTest.php b/tests/Feature/QmsProTest.php index ca7e61a..225b35b 100644 --- a/tests/Feature/QmsProTest.php +++ b/tests/Feature/QmsProTest.php @@ -54,6 +54,8 @@ class QmsProTest extends TestCase ->assertSee('GHS '.$proPerBranch) ->assertSee('GHS '.$enterprisePerBranch) ->assertSee('/branch/mo') + ->assertSee('Branches') + ->assertSee('Minimum is your current active branch count') ->assertSee('Upgrade to Pro') ->assertSee('Enterprise'); } @@ -109,7 +111,7 @@ class QmsProTest extends TestCase ]); $this->actingAs($this->owner) - ->post(route('qms.pro.subscribe')) + ->post(route('qms.pro.subscribe'), ['branches' => 1]) ->assertRedirect(route('qms.pro.index')) ->assertSessionHas('success'); @@ -118,6 +120,26 @@ class QmsProTest extends TestCase $this->assertSame(1, $settings['pro_billed_branches']); } + public function test_subscribe_pro_uses_selected_branch_count(): void + { + Http::fake([ + 'billing.test/can-afford*' => Http::response(['affordable' => true]), + 'billing.test/debit' => function ($request) { + $expected = 3 * (int) config('qms.plans.pro.price_minor_per_branch'); + $this->assertSame($expected, (int) $request['amount_minor']); + + return Http::response(['ok' => true]); + }, + ]); + + $this->actingAs($this->owner) + ->post(route('qms.pro.subscribe'), ['branches' => 3]) + ->assertRedirect(route('qms.pro.index')) + ->assertSessionHas('success'); + + $this->assertSame(3, $this->organization->fresh()->settings['pro_billed_branches']); + } + public function test_subscribe_enterprise_requires_multiple_branches(): void { Http::fake([ @@ -126,7 +148,7 @@ class QmsProTest extends TestCase ]); $this->actingAs($this->owner) - ->post(route('qms.pro.subscribe-enterprise')) + ->post(route('qms.pro.subscribe-enterprise'), ['branches' => 1]) ->assertRedirect(route('qms.pro.index')) ->assertSessionHas('error'); @@ -138,7 +160,7 @@ class QmsProTest extends TestCase ]); $this->actingAs($this->owner) - ->post(route('qms.pro.subscribe-enterprise')) + ->post(route('qms.pro.subscribe-enterprise'), ['branches' => 2]) ->assertRedirect(route('qms.pro.index')) ->assertSessionHas('success'); @@ -259,6 +281,7 @@ class QmsProTest extends TestCase ->post(route('qms.pro.subscribe-prepaid'), [ 'plan' => 'pro', 'months' => 12, + 'branches' => 1, ]) ->assertRedirect('https://checkout.paystack.test/pay'); }