Files
ladill-care/app/Services/Care/PlanService.php
T
isaacclad f9aa4a679f
Deploy Ladill Care / deploy (push) Successful in 29s
Tighten Care free tier and bill Pro/Enterprise per branch.
Free keeps core records, appointments, and basic workflows; Pro unlocks lab, pharmacy, billing, and Queue. Both paid tiers bill per branch with unlimited branches. Enterprise adds multi-dept workflows, analytics, AI-assisted healthcare integration, and priority support (not Afia).
2026-07-15 14:14:35 +00:00

142 lines
4.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services\Care;
use App\Models\Branch;
use App\Models\Organization;
use Carbon\Carbon;
class PlanService
{
public function planKey(Organization $organization): string
{
$settings = $organization->settings ?? [];
$plan = (string) ($settings['plan'] ?? 'free');
if (in_array($plan, ['pro', 'enterprise'], true) && ! empty($settings['plan_expires_at'])) {
if (Carbon::parse($settings['plan_expires_at'])->isPast()) {
return 'free';
}
}
return array_key_exists($plan, config('care.plans', [])) ? $plan : 'free';
}
public function plan(Organization $organization): string
{
return $this->planKey($organization);
}
public function isPro(Organization $organization): bool
{
return $this->planKey($organization) === 'pro';
}
public function isEnterprise(Organization $organization): bool
{
return $this->planKey($organization) === 'enterprise';
}
public function hasPaidPlan(Organization $organization): bool
{
return in_array($this->planKey($organization), ['pro', 'enterprise'], true);
}
/** Lab, pharmacy, billing, and Ladill Queue integration require Pro or Enterprise. */
public function canUseProModules(Organization $organization): bool
{
return $this->hasPaidPlan($organization);
}
public function canUseQueueIntegration(Organization $organization): bool
{
return $this->hasPaidPlan($organization);
}
public function activeBranchCount(Organization $organization): int
{
return Branch::query()
->where('organization_id', $organization->id)
->where('is_active', true)
->count();
}
public function maxBranches(Organization $organization): ?int
{
$plan = config('care.plans.'.$this->planKey($organization));
$max = $plan['max_branches'] ?? null;
return $max === null ? null : (int) $max;
}
public function canAddBranch(Organization $organization, int $currentCount): bool
{
$max = $this->maxBranches($organization);
return $max === null || $currentCount < $max;
}
public function branchLimitMessage(Organization $organization): string
{
$max = $this->maxBranches($organization);
$key = $this->planKey($organization);
if ($key === 'free') {
return 'Your Free plan allows 1 branch. Upgrade to Care Pro for unlimited branches (billed per branch).';
}
if ($key === 'pro' && $max !== null) {
return "Your Pro plan allows up to {$max} branches (billed per branch).";
}
return 'Branch limit reached for your current plan.';
}
public function proPricePerBranchMinor(): int
{
return (int) config(
'care.plans.pro.price_minor_per_branch',
config('care.plans.pro.price_minor', 199000),
);
}
/** @deprecated Prefer proPricePerBranchMinor() — Pro is billed per branch. */
public function proPriceMinor(): int
{
return $this->proPricePerBranchMinor();
}
public function enterprisePricePerBranchMinor(): int
{
return (int) config(
'care.plans.enterprise.price_minor_per_branch',
config('care.plans.enterprise.price_minor', 499000),
);
}
/** Total monthly amount for Pro = active branches × per-branch rate (min 1). */
public function proPriceTotalMinor(Organization $organization): int
{
return max(1, $this->activeBranchCount($organization)) * $this->proPricePerBranchMinor();
}
/** Total monthly amount for Enterprise = active branches × per-branch rate (min 1). */
public function enterprisePriceMinor(Organization $organization): int
{
return max(1, $this->activeBranchCount($organization)) * $this->enterprisePricePerBranchMinor();
}
public function monthlyPriceMinor(Organization $organization, string $plan): int
{
return $plan === 'enterprise'
? $this->enterprisePriceMinor($organization)
: $this->proPriceTotalMinor($organization);
}
public function canSubscribeEnterprise(Organization $organization): bool
{
// Enterprise is a feature tier (not branch-gated). min_branches defaults to 1.
return $this->activeBranchCount($organization) >= (int) config('care.enterprise.min_branches', 1);
}
}