Bill Queue Pro per active branch like Enterprise.
Deploy Ladill Queue / deploy (push) Successful in 33s
Deploy Ladill Queue / deploy (push) Successful in 33s
Show /branch/mo pricing with live branch totals on the plans page, charge subscribe/prepaid/renewals by branch count, and store pro_billed_branches.
This commit is contained in:
@@ -27,7 +27,11 @@ class ProController extends Controller
|
|||||||
: null;
|
: null;
|
||||||
$planKey = $plans->planKey($organization);
|
$planKey = $plans->planKey($organization);
|
||||||
$branchCount = $plans->activeBranchCount($organization);
|
$branchCount = $plans->activeBranchCount($organization);
|
||||||
|
$proPrice = $plans->proPriceMinor($organization);
|
||||||
$enterprisePrice = $plans->enterprisePriceMinor($organization);
|
$enterprisePrice = $plans->enterprisePriceMinor($organization);
|
||||||
|
$billedBranches = (int) ($settings['pro_billed_branches']
|
||||||
|
?? $settings['enterprise_billed_branches']
|
||||||
|
?? $branchCount);
|
||||||
|
|
||||||
return view('qms.pro.index', [
|
return view('qms.pro.index', [
|
||||||
'organization' => $organization,
|
'organization' => $organization,
|
||||||
@@ -36,7 +40,8 @@ class ProController extends Controller
|
|||||||
'isEnterprise' => $planKey === 'enterprise',
|
'isEnterprise' => $planKey === 'enterprise',
|
||||||
'hasPaidPlan' => $plans->hasPaidPlan($organization),
|
'hasPaidPlan' => $plans->hasPaidPlan($organization),
|
||||||
'canManage' => $canManage,
|
'canManage' => $canManage,
|
||||||
'proPriceMinor' => $plans->proPriceMinor(),
|
'proPricePerBranchMinor' => $plans->proPricePerBranchMinor(),
|
||||||
|
'proPriceMinor' => $proPrice,
|
||||||
'enterprisePricePerBranchMinor' => $plans->enterprisePricePerBranchMinor(),
|
'enterprisePricePerBranchMinor' => $plans->enterprisePricePerBranchMinor(),
|
||||||
'enterprisePriceMinor' => $enterprisePrice,
|
'enterprisePriceMinor' => $enterprisePrice,
|
||||||
'branchCount' => $branchCount,
|
'branchCount' => $branchCount,
|
||||||
@@ -45,7 +50,9 @@ class ProController extends Controller
|
|||||||
'prepaidMonths' => (array) config('qms.prepaid_months', [6, 12, 24]),
|
'prepaidMonths' => (array) config('qms.prepaid_months', [6, 12, 24]),
|
||||||
'currency' => (string) config('billing.currency', 'GHS'),
|
'currency' => (string) config('billing.currency', 'GHS'),
|
||||||
'planExpiresAt' => $expiresAt,
|
'planExpiresAt' => $expiresAt,
|
||||||
|
'proBilledBranches' => (int) ($settings['pro_billed_branches'] ?? $branchCount),
|
||||||
'enterpriseBilledBranches' => (int) ($settings['enterprise_billed_branches'] ?? $branchCount),
|
'enterpriseBilledBranches' => (int) ($settings['enterprise_billed_branches'] ?? $branchCount),
|
||||||
|
'billedBranches' => $billedBranches,
|
||||||
'salesUrl' => ladill_platform_url('contact-sales'),
|
'salesUrl' => ladill_platform_url('contact-sales'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -63,12 +70,13 @@ class ProController extends Controller
|
|||||||
return $this->chargeMonthlyWallet(
|
return $this->chargeMonthlyWallet(
|
||||||
$organization,
|
$organization,
|
||||||
$billing,
|
$billing,
|
||||||
$plans->proPriceMinor(),
|
$plans->proPriceMinor($organization),
|
||||||
'pro',
|
'pro',
|
||||||
'queue_pro',
|
'queue_pro',
|
||||||
'queue-pro-'.$organization->id.'-'.now()->format('Y-m-d-His'),
|
'queue-pro-'.$organization->id.'-'.now()->format('Y-m-d-His'),
|
||||||
'Ladill Queue Pro — monthly subscription',
|
'Ladill Queue Pro — monthly subscription',
|
||||||
'Welcome to Queue Pro — active for one month.',
|
'Welcome to Queue Pro — active for one month.',
|
||||||
|
$plans->activeBranchCount($organization),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,9 +136,19 @@ class ProController extends Controller
|
|||||||
|
|
||||||
$monthlyMinor = $plan === 'enterprise'
|
$monthlyMinor = $plan === 'enterprise'
|
||||||
? $plans->enterprisePriceMinor($organization)
|
? $plans->enterprisePriceMinor($organization)
|
||||||
: $plans->proPriceMinor();
|
: $plans->proPriceMinor($organization);
|
||||||
$amountMinor = $monthlyMinor * $months;
|
$amountMinor = $monthlyMinor * $months;
|
||||||
$branches = $plan === 'enterprise' ? $plans->activeBranchCount($organization) : null;
|
$branches = $plans->activeBranchCount($organization);
|
||||||
|
|
||||||
|
$metadata = [
|
||||||
|
'organization_id' => $organization->id,
|
||||||
|
'billed_branches' => $branches,
|
||||||
|
];
|
||||||
|
if ($plan === 'enterprise') {
|
||||||
|
$metadata['enterprise_billed_branches'] = $branches;
|
||||||
|
} else {
|
||||||
|
$metadata['pro_billed_branches'] = $branches;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$checkout = $billing->initiatePlanCheckout(
|
$checkout = $billing->initiatePlanCheckout(
|
||||||
@@ -139,10 +157,7 @@ class ProController extends Controller
|
|||||||
$months,
|
$months,
|
||||||
$amountMinor,
|
$amountMinor,
|
||||||
route('qms.pro.paystack.callback'),
|
route('qms.pro.paystack.callback'),
|
||||||
array_filter([
|
$metadata,
|
||||||
'organization_id' => $organization->id,
|
|
||||||
'enterprise_billed_branches' => $branches,
|
|
||||||
], static fn ($v) => $v !== null),
|
|
||||||
);
|
);
|
||||||
} catch (\Throwable) {
|
} catch (\Throwable) {
|
||||||
return redirect()->route('qms.pro.index')
|
return redirect()->route('qms.pro.index')
|
||||||
@@ -191,9 +206,13 @@ class ProController extends Controller
|
|||||||
|
|
||||||
$plan = (string) ($result['plan'] ?? 'pro');
|
$plan = (string) ($result['plan'] ?? 'pro');
|
||||||
$months = (int) ($result['months'] ?? 0);
|
$months = (int) ($result['months'] ?? 0);
|
||||||
$branches = isset($metadata['enterprise_billed_branches'])
|
$branches = isset($metadata['billed_branches'])
|
||||||
? (int) $metadata['enterprise_billed_branches']
|
? (int) $metadata['billed_branches']
|
||||||
: null;
|
: (isset($metadata['pro_billed_branches'])
|
||||||
|
? (int) $metadata['pro_billed_branches']
|
||||||
|
: (isset($metadata['enterprise_billed_branches'])
|
||||||
|
? (int) $metadata['enterprise_billed_branches']
|
||||||
|
: null));
|
||||||
|
|
||||||
$this->activatePrepaidPlan($organization, $plan, $months, $branches);
|
$this->activatePrepaidPlan($organization, $plan, $months, $branches);
|
||||||
|
|
||||||
@@ -212,7 +231,7 @@ class ProController extends Controller
|
|||||||
string $reference,
|
string $reference,
|
||||||
string $description,
|
string $description,
|
||||||
string $successMessage,
|
string $successMessage,
|
||||||
?int $enterpriseBranches = null,
|
?int $billedBranches = null,
|
||||||
): RedirectResponse {
|
): RedirectResponse {
|
||||||
try {
|
try {
|
||||||
if (! $billing->canAfford($organization->owner_ref, $priceMinor)) {
|
if (! $billing->canAfford($organization->owner_ref, $priceMinor)) {
|
||||||
@@ -244,9 +263,7 @@ class ProController extends Controller
|
|||||||
$settings['plan_expires_at'] = now()
|
$settings['plan_expires_at'] = now()
|
||||||
->addDays((int) config('qms.pro.period_days', 30))
|
->addDays((int) config('qms.pro.period_days', 30))
|
||||||
->toIso8601String();
|
->toIso8601String();
|
||||||
if ($enterpriseBranches !== null) {
|
$this->applyBilledBranches($settings, $plan, $billedBranches);
|
||||||
$settings['enterprise_billed_branches'] = $enterpriseBranches;
|
|
||||||
}
|
|
||||||
unset($settings['plan_renewal_error']);
|
unset($settings['plan_renewal_error']);
|
||||||
$organization->update(['settings' => $settings]);
|
$organization->update(['settings' => $settings]);
|
||||||
|
|
||||||
@@ -257,17 +274,33 @@ class ProController extends Controller
|
|||||||
Organization $organization,
|
Organization $organization,
|
||||||
string $plan,
|
string $plan,
|
||||||
int $months,
|
int $months,
|
||||||
?int $enterpriseBranches = null,
|
?int $billedBranches = null,
|
||||||
): void {
|
): void {
|
||||||
$settings = $organization->settings ?? [];
|
$settings = $organization->settings ?? [];
|
||||||
$settings['plan'] = $plan;
|
$settings['plan'] = $plan;
|
||||||
$settings['auto_renew'] = false;
|
$settings['auto_renew'] = false;
|
||||||
$settings['billing_method'] = 'paystack_prepaid';
|
$settings['billing_method'] = 'paystack_prepaid';
|
||||||
$settings['plan_expires_at'] = now()->addMonths($months)->toIso8601String();
|
$settings['plan_expires_at'] = now()->addMonths($months)->toIso8601String();
|
||||||
if ($enterpriseBranches !== null) {
|
$this->applyBilledBranches($settings, $plan, $billedBranches);
|
||||||
$settings['enterprise_billed_branches'] = $enterpriseBranches;
|
|
||||||
}
|
|
||||||
unset($settings['plan_renewal_error']);
|
unset($settings['plan_renewal_error']);
|
||||||
$organization->update(['settings' => $settings]);
|
$organization->update(['settings' => $settings]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $settings
|
||||||
|
*/
|
||||||
|
private function applyBilledBranches(array &$settings, string $plan, ?int $billedBranches): void
|
||||||
|
{
|
||||||
|
if ($billedBranches === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($plan === 'enterprise') {
|
||||||
|
$settings['enterprise_billed_branches'] = $billedBranches;
|
||||||
|
unset($settings['pro_billed_branches']);
|
||||||
|
} else {
|
||||||
|
$settings['pro_billed_branches'] = $billedBranches;
|
||||||
|
unset($settings['enterprise_billed_branches']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,9 +74,19 @@ class PlanService
|
|||||||
return $max === null || $currentCount < $max;
|
return $max === null || $currentCount < $max;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function proPriceMinor(): int
|
public function proPricePerBranchMinor(): int
|
||||||
{
|
{
|
||||||
return (int) config('qms.plans.pro.price_minor', 249000);
|
return (int) config(
|
||||||
|
'qms.plans.pro.price_minor_per_branch',
|
||||||
|
config('qms.plans.pro.price_minor', 249000)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function proPriceMinor(Organization $organization): int
|
||||||
|
{
|
||||||
|
$branches = max(1, $this->activeBranchCount($organization));
|
||||||
|
|
||||||
|
return $branches * $this->proPricePerBranchMinor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enterprisePricePerBranchMinor(): int
|
public function enterprisePricePerBranchMinor(): int
|
||||||
|
|||||||
@@ -52,9 +52,10 @@ class ProRenewalService
|
|||||||
$isEnterprise = $plan === 'enterprise';
|
$isEnterprise = $plan === 'enterprise';
|
||||||
$price = $isEnterprise
|
$price = $isEnterprise
|
||||||
? $this->plans->enterprisePriceMinor($organization)
|
? $this->plans->enterprisePriceMinor($organization)
|
||||||
: $this->plans->proPriceMinor();
|
: $this->plans->proPriceMinor($organization);
|
||||||
$reference = ($isEnterprise ? 'queue-enterprise-' : 'queue-pro-')
|
$reference = ($isEnterprise ? 'queue-enterprise-' : 'queue-pro-')
|
||||||
.$organization->id.'-'.now()->format('YmdHis');
|
.$organization->id.'-'.now()->format('YmdHis');
|
||||||
|
$branchCount = $this->plans->activeBranchCount($organization);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$charged = $this->billing->debit(
|
$charged = $this->billing->debit(
|
||||||
@@ -77,7 +78,11 @@ class ProRenewalService
|
|||||||
->addDays((int) config('qms.pro.period_days', 30))
|
->addDays((int) config('qms.pro.period_days', 30))
|
||||||
->toIso8601String();
|
->toIso8601String();
|
||||||
if ($isEnterprise) {
|
if ($isEnterprise) {
|
||||||
$settings['enterprise_billed_branches'] = $this->plans->activeBranchCount($organization);
|
$settings['enterprise_billed_branches'] = $branchCount;
|
||||||
|
unset($settings['pro_billed_branches']);
|
||||||
|
} else {
|
||||||
|
$settings['pro_billed_branches'] = $branchCount;
|
||||||
|
unset($settings['enterprise_billed_branches']);
|
||||||
}
|
}
|
||||||
unset($settings['plan_renewal_error']);
|
unset($settings['plan_renewal_error']);
|
||||||
$organization->update(['settings' => $settings]);
|
$organization->update(['settings' => $settings]);
|
||||||
@@ -89,7 +94,11 @@ class ProRenewalService
|
|||||||
$graceEnd = $expires->copy()->addDays((int) config('qms.pro.grace_days', 3));
|
$graceEnd = $expires->copy()->addDays((int) config('qms.pro.grace_days', 3));
|
||||||
if ($graceEnd->isPast()) {
|
if ($graceEnd->isPast()) {
|
||||||
$settings['plan'] = 'free';
|
$settings['plan'] = 'free';
|
||||||
unset($settings['plan_expires_at'], $settings['enterprise_billed_branches']);
|
unset(
|
||||||
|
$settings['plan_expires_at'],
|
||||||
|
$settings['enterprise_billed_branches'],
|
||||||
|
$settings['pro_billed_branches'],
|
||||||
|
);
|
||||||
$settings['plan_renewal_error'] = 'Suspended after failed renewal.';
|
$settings['plan_renewal_error'] = 'Suspended after failed renewal.';
|
||||||
} else {
|
} else {
|
||||||
$settings['plan_renewal_error'] = 'Renewal failed — top up your wallet.';
|
$settings['plan_renewal_error'] = 'Renewal failed — top up your wallet.';
|
||||||
|
|||||||
+5
-2
@@ -183,7 +183,10 @@ return [
|
|||||||
],
|
],
|
||||||
'pro' => [
|
'pro' => [
|
||||||
'label' => 'Pro',
|
'label' => 'Pro',
|
||||||
'price_minor' => (int) env('QUEUE_PRO_PRICE_MINOR', 249000),
|
'price_minor_per_branch' => (int) env(
|
||||||
|
'QUEUE_PRO_PRICE_PER_BRANCH_MINOR',
|
||||||
|
env('QUEUE_PRO_PRICE_MINOR', 249000)
|
||||||
|
),
|
||||||
'max_branches' => null,
|
'max_branches' => null,
|
||||||
'max_queues' => null,
|
'max_queues' => null,
|
||||||
],
|
],
|
||||||
@@ -249,7 +252,7 @@ return [
|
|||||||
|
|
||||||
'upgrade_banner' => [
|
'upgrade_banner' => [
|
||||||
'title' => 'Unlock Queue Pro or Enterprise',
|
'title' => 'Unlock Queue Pro or Enterprise',
|
||||||
'description' => 'Unlimited branches, advanced routing, Care & Frontdesk integrations — from GHS 2490/mo.',
|
'description' => 'Unlimited branches, advanced routing, Care & Frontdesk integrations — from GHS 2490/branch/mo.',
|
||||||
'route' => 'qms.pro.index',
|
'route' => 'qms.pro.index',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<x-app-layout title="Plans" heading="Queue plans">
|
<x-app-layout title="Plans" heading="Queue plans">
|
||||||
@php
|
@php
|
||||||
$proPrice = number_format($proPriceMinor / 100, 0);
|
$proPerBranch = number_format($proPricePerBranchMinor / 100, 0);
|
||||||
|
$proTotal = number_format($proPriceMinor / 100, 0);
|
||||||
$enterprisePerBranch = number_format($enterprisePricePerBranchMinor / 100, 0);
|
$enterprisePerBranch = number_format($enterprisePricePerBranchMinor / 100, 0);
|
||||||
$enterpriseTotal = number_format($enterprisePriceMinor / 100, 0);
|
$enterpriseTotal = number_format($enterprisePriceMinor / 100, 0);
|
||||||
@endphp
|
@endphp
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
<h1 class="text-2xl font-bold text-slate-900">Choose your Queue plan</h1>
|
<h1 class="text-2xl font-bold text-slate-900">Choose your Queue plan</h1>
|
||||||
<p class="mt-1 text-sm text-slate-600">
|
<p class="mt-1 text-sm text-slate-600">
|
||||||
Pay monthly from your Ladill wallet, or prepay 6/12/24 months via Paystack.
|
Pay monthly from your Ladill wallet, or prepay 6/12/24 months via Paystack.
|
||||||
|
Pro and Enterprise are billed per active branch.
|
||||||
Enterprise includes a one-time setup fee — <a href="{{ $salesUrl }}" class="font-medium text-indigo-600 hover:text-indigo-800" target="_blank" rel="noopener">contact sales</a> for onboarding.
|
Enterprise includes a one-time setup fee — <a href="{{ $salesUrl }}" class="font-medium text-indigo-600 hover:text-indigo-800" target="_blank" rel="noopener">contact sales</a> for onboarding.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-4 flex flex-wrap gap-2">
|
<div class="mt-4 flex flex-wrap gap-2">
|
||||||
@@ -50,8 +52,20 @@
|
|||||||
{{-- Pro --}}
|
{{-- Pro --}}
|
||||||
<div class="flex flex-col rounded-2xl border border-slate-200 bg-white p-6 {{ $planKey === 'pro' ? 'ring-2 ring-indigo-500' : '' }}">
|
<div class="flex flex-col rounded-2xl border border-slate-200 bg-white p-6 {{ $planKey === 'pro' ? 'ring-2 ring-indigo-500' : '' }}">
|
||||||
<p class="text-xs font-semibold uppercase tracking-wide text-indigo-600">Pro</p>
|
<p class="text-xs font-semibold uppercase tracking-wide text-indigo-600">Pro</p>
|
||||||
<x-plan-tier-price :currency="$currency" :monthly-minor="$proPriceMinor" />
|
<x-plan-tier-price
|
||||||
<p class="mt-1 text-sm text-slate-500" x-show="billing === 'monthly'">Unlimited branches & queues</p>
|
:currency="$currency"
|
||||||
|
:monthly-minor="$proPricePerBranchMinor"
|
||||||
|
:prepaid-minor="$proPriceMinor"
|
||||||
|
:monthly-display="$proPerBranch"
|
||||||
|
monthly-suffix="/branch/mo"
|
||||||
|
/>
|
||||||
|
<p class="mt-1 text-sm text-slate-500" x-show="billing === 'monthly'">
|
||||||
|
@if ($branchCount > 0)
|
||||||
|
{{ $branchCount }} active {{ str('branch')->plural($branchCount) }} = {{ $currency }} {{ $proTotal }}/mo
|
||||||
|
@else
|
||||||
|
Per-branch billing
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
<ul class="mt-5 flex-1 space-y-2 text-sm text-slate-700">
|
<ul class="mt-5 flex-1 space-y-2 text-sm text-slate-700">
|
||||||
<li>Advanced routing rules</li>
|
<li>Advanced routing rules</li>
|
||||||
<li>Unlimited kiosks & displays</li>
|
<li>Unlimited kiosks & displays</li>
|
||||||
@@ -66,19 +80,25 @@
|
|||||||
@if ($planExpiresAt)
|
@if ($planExpiresAt)
|
||||||
until <strong>{{ $planExpiresAt->format('d M Y') }}</strong>
|
until <strong>{{ $planExpiresAt->format('d M Y') }}</strong>
|
||||||
@endif
|
@endif
|
||||||
|
@if ($proBilledBranches > 0)
|
||||||
|
· billed for {{ $proBilledBranches }} {{ str('branch')->plural($proBilledBranches) }}
|
||||||
|
@endif
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
@elseif ($canManage)
|
@elseif ($canManage)
|
||||||
<form x-show="billing === 'monthly'" method="post" action="{{ route('qms.pro.subscribe') }}">
|
<form x-show="billing === 'monthly'" method="post" action="{{ route('qms.pro.subscribe') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<button class="btn-primary w-full">Upgrade to Pro — monthly wallet</button>
|
<button class="btn-primary w-full">
|
||||||
|
Upgrade to Pro — {{ $currency }} {{ $proTotal }}/mo wallet
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<template x-for="months in @js($prepaidMonths)" :key="months">
|
<template x-for="months in @js($prepaidMonths)" :key="months">
|
||||||
<form x-show="billing == months" method="post" action="{{ route('qms.pro.subscribe-prepaid') }}">
|
<form x-show="billing == months" method="post" action="{{ route('qms.pro.subscribe-prepaid') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="plan" value="pro">
|
<input type="hidden" name="plan" value="pro">
|
||||||
<input type="hidden" name="months" :value="months">
|
<input type="hidden" name="months" :value="months">
|
||||||
<button type="submit" class="btn-primary w-full" x-text="'Pay ' + months + ' months via Paystack'"></button>
|
<button type="submit" class="btn-primary w-full"
|
||||||
|
x-text="'Pay ' + months + ' months via Paystack — {{ $currency }} ' + ({{ $proPriceMinor }} * months / 100).toLocaleString()"></button>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
@else
|
@else
|
||||||
|
|||||||
@@ -44,12 +44,16 @@ class QmsProTest extends TestCase
|
|||||||
|
|
||||||
public function test_pro_page_renders_three_tier_pricing(): void
|
public function test_pro_page_renders_three_tier_pricing(): void
|
||||||
{
|
{
|
||||||
|
$proPerBranch = number_format((int) config('qms.plans.pro.price_minor_per_branch') / 100, 0);
|
||||||
|
$enterprisePerBranch = number_format((int) config('qms.plans.enterprise.price_minor_per_branch') / 100, 0);
|
||||||
|
|
||||||
$this->actingAs($this->owner)
|
$this->actingAs($this->owner)
|
||||||
->get(route('qms.pro.index'))
|
->get(route('qms.pro.index'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Choose your Queue plan')
|
->assertSee('Choose your Queue plan')
|
||||||
->assertSee('GHS 99')
|
->assertSee('GHS '.$proPerBranch)
|
||||||
->assertSee('GHS 299')
|
->assertSee('GHS '.$enterprisePerBranch)
|
||||||
|
->assertSee('/branch/mo')
|
||||||
->assertSee('Upgrade to Pro')
|
->assertSee('Upgrade to Pro')
|
||||||
->assertSee('Enterprise');
|
->assertSee('Enterprise');
|
||||||
}
|
}
|
||||||
@@ -109,7 +113,9 @@ class QmsProTest extends TestCase
|
|||||||
->assertRedirect(route('qms.pro.index'))
|
->assertRedirect(route('qms.pro.index'))
|
||||||
->assertSessionHas('success');
|
->assertSessionHas('success');
|
||||||
|
|
||||||
$this->assertSame('pro', $this->organization->fresh()->settings['plan']);
|
$settings = $this->organization->fresh()->settings;
|
||||||
|
$this->assertSame('pro', $settings['plan']);
|
||||||
|
$this->assertSame(1, $settings['pro_billed_branches']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_subscribe_enterprise_requires_multiple_branches(): void
|
public function test_subscribe_enterprise_requires_multiple_branches(): void
|
||||||
@@ -147,21 +153,64 @@ class QmsProTest extends TestCase
|
|||||||
'settings' => array_merge($this->organization->settings ?? [], [
|
'settings' => array_merge($this->organization->settings ?? [], [
|
||||||
'plan' => 'pro',
|
'plan' => 'pro',
|
||||||
'auto_renew' => true,
|
'auto_renew' => true,
|
||||||
|
'pro_billed_branches' => 1,
|
||||||
'plan_expires_at' => now()->subDay()->toIso8601String(),
|
'plan_expires_at' => now()->subDay()->toIso8601String(),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$expected = (int) config('qms.plans.pro.price_minor_per_branch');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'billing.test/debit' => Http::response(['ok' => true]),
|
'billing.test/debit' => function ($request) use ($expected) {
|
||||||
|
$this->assertSame($expected, (int) $request['amount_minor']);
|
||||||
|
|
||||||
|
return Http::response(['ok' => true]);
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->artisan('qms:pro-renew')->assertSuccessful();
|
$this->artisan('qms:pro-renew')->assertSuccessful();
|
||||||
|
|
||||||
$settings = $this->organization->fresh()->settings;
|
$settings = $this->organization->fresh()->settings;
|
||||||
$this->assertSame('pro', $settings['plan']);
|
$this->assertSame('pro', $settings['plan']);
|
||||||
|
$this->assertSame(1, $settings['pro_billed_branches']);
|
||||||
$this->assertTrue(now()->parse($settings['plan_expires_at'])->isFuture());
|
$this->assertTrue(now()->parse($settings['plan_expires_at'])->isFuture());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_pro_renew_charges_per_active_branch(): void
|
||||||
|
{
|
||||||
|
Branch::create([
|
||||||
|
'owner_ref' => $this->organization->owner_ref,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'name' => 'Second',
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->organization->update([
|
||||||
|
'settings' => array_merge($this->organization->settings ?? [], [
|
||||||
|
'plan' => 'pro',
|
||||||
|
'auto_renew' => true,
|
||||||
|
'pro_billed_branches' => 1,
|
||||||
|
'plan_expires_at' => now()->subDay()->toIso8601String(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$expected = 2 * (int) config('qms.plans.pro.price_minor_per_branch');
|
||||||
|
|
||||||
|
Http::fake([
|
||||||
|
'billing.test/debit' => function ($request) use ($expected) {
|
||||||
|
$this->assertSame($expected, (int) $request['amount_minor']);
|
||||||
|
|
||||||
|
return Http::response(['ok' => true]);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->artisan('qms:pro-renew')->assertSuccessful();
|
||||||
|
|
||||||
|
$settings = $this->organization->fresh()->settings;
|
||||||
|
$this->assertSame('pro', $settings['plan']);
|
||||||
|
$this->assertSame(2, $settings['pro_billed_branches']);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_enterprise_renew_charges_per_active_branch(): void
|
public function test_enterprise_renew_charges_per_active_branch(): void
|
||||||
{
|
{
|
||||||
Branch::create([
|
Branch::create([
|
||||||
@@ -180,9 +229,11 @@ class QmsProTest extends TestCase
|
|||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$expected = 2 * (int) config('qms.plans.enterprise.price_minor_per_branch');
|
||||||
|
|
||||||
Http::fake([
|
Http::fake([
|
||||||
'billing.test/debit' => function ($request) {
|
'billing.test/debit' => function ($request) use ($expected) {
|
||||||
$this->assertSame(59800, (int) $request['amount_minor']);
|
$this->assertSame($expected, (int) $request['amount_minor']);
|
||||||
|
|
||||||
return Http::response(['ok' => true]);
|
return Http::response(['ok' => true]);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user