diff --git a/app/Http/Controllers/Care/BranchController.php b/app/Http/Controllers/Care/BranchController.php index b35fbb9..4be394b 100644 --- a/app/Http/Controllers/Care/BranchController.php +++ b/app/Http/Controllers/Care/BranchController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Care; use App\Http\Controllers\Controller; +use App\Http\Controllers\Care\Concerns\RequiresPlanFeature; use App\Http\Controllers\Care\Concerns\ScopesToAccount; use App\Models\Branch; use App\Services\Care\AuditLogger; @@ -13,11 +14,26 @@ use Illuminate\View\View; class BranchController extends Controller { + use RequiresPlanFeature; use ScopesToAccount; + private const FEATURE = 'branches'; + + private const UPGRADE_MESSAGE = 'Multi-branch management requires Care Pro.'; + public function index(Request $request): View { $this->authorizeAbility($request, 'admin.branches.view'); + + if ($upgrade = $this->proFeatureUpgradeView( + $request, + self::FEATURE, + 'Branches', + 'Manage clinic locations and sites with Care Pro. Free plans include one branch created during setup.', + )) { + return $upgrade; + } + $organization = $this->organization($request); $branches = Branch::owned($this->ownerRef($request)) @@ -39,12 +55,26 @@ class BranchController extends Controller { $this->authorizeAbility($request, 'admin.branches.manage'); + if ($upgrade = $this->proFeatureUpgradeView( + $request, + self::FEATURE, + 'Branches', + 'Add and manage multiple clinic locations with Care Pro.', + )) { + return $upgrade; + } + return view('care.admin.branches.create', ['organization' => $this->organization($request)]); } public function store(Request $request): RedirectResponse { $this->authorizeAbility($request, 'admin.branches.manage'); + + if ($deny = $this->denyWithoutFeature($request, self::FEATURE, self::UPGRADE_MESSAGE)) { + return $deny; + } + $organization = $this->organization($request); $owner = $this->ownerRef($request); @@ -83,6 +113,15 @@ class BranchController extends Controller $this->authorizeAbility($request, 'admin.branches.manage'); $this->authorizeOwner($request, $branch); + if ($upgrade = $this->proFeatureUpgradeView( + $request, + self::FEATURE, + 'Branches', + 'Edit clinic locations with Care Pro. Free plans include one branch created during setup.', + )) { + return $upgrade; + } + return view('care.admin.branches.edit', compact('branch')); } @@ -91,6 +130,10 @@ class BranchController extends Controller $this->authorizeAbility($request, 'admin.branches.manage'); $this->authorizeOwner($request, $branch); + if ($deny = $this->denyWithoutFeature($request, self::FEATURE, self::UPGRADE_MESSAGE)) { + return $deny; + } + $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'code' => ['nullable', 'string', 'max:50'], diff --git a/app/Http/Controllers/Care/Concerns/RequiresPlanFeature.php b/app/Http/Controllers/Care/Concerns/RequiresPlanFeature.php new file mode 100644 index 0000000..1ecf48e --- /dev/null +++ b/app/Http/Controllers/Care/Concerns/RequiresPlanFeature.php @@ -0,0 +1,53 @@ +organization($request); + $plans = app(PlanService::class); + + if ($plans->hasFeature($organization, $feature)) { + return null; + } + + return view('care.settings.pro-feature', [ + 'organization' => $organization, + 'title' => $title, + 'description' => $description, + 'proPriceMinor' => $plans->proPricePerBranchMinor(), + ]); + } + + /** + * For mutations: redirect free-plan orgs to the plans page. + */ + protected function denyWithoutFeature( + Request $request, + string $feature, + string $message, + ): ?RedirectResponse { + $organization = $this->organization($request); + if (app(PlanService::class)->hasFeature($organization, $feature)) { + return null; + } + + return redirect() + ->route('care.pro.index') + ->with('upsell', $message); + } +} diff --git a/app/Http/Controllers/Care/SettingsController.php b/app/Http/Controllers/Care/SettingsController.php index 7b2023a..987fe6d 100644 --- a/app/Http/Controllers/Care/SettingsController.php +++ b/app/Http/Controllers/Care/SettingsController.php @@ -34,7 +34,6 @@ class SettingsController extends Controller ->count(); $plans = app(PlanService::class); - $hasPaidPlan = $plans->hasPaidPlan($organization); return view('care.settings.edit', [ 'organization' => $organization, @@ -42,7 +41,7 @@ class SettingsController extends Controller 'branchCount' => $branchCount, 'canViewBranches' => $permissions->can($member, 'admin.branches.view'), 'canViewTeam' => $permissions->can($member, 'admin.members.view'), - 'hasPaidPlan' => $hasPaidPlan, + 'hasBranchesFeature' => $plans->hasFeature($organization, 'branches'), 'canUseQueueIntegration' => $plans->canUseQueueIntegration($organization), 'messagingSmsReady' => $credential->hasValidSms(), 'messagingEmailReady' => $credential->hasValidBird(), diff --git a/app/Services/Care/PlanService.php b/app/Services/Care/PlanService.php index 2a50a64..12e694f 100644 --- a/app/Services/Care/PlanService.php +++ b/app/Services/Care/PlanService.php @@ -53,6 +53,13 @@ class PlanService return $this->hasPaidPlan($organization); } + public function hasFeature(Organization $organization, string $feature): bool + { + $features = config('care.plans.'.$this->planKey($organization).'.features', []); + + return in_array($feature, $features, true); + } + public function activeBranchCount(Organization $organization): int { return Branch::query() diff --git a/config/care.php b/config/care.php index dacdf3a..2213270 100644 --- a/config/care.php +++ b/config/care.php @@ -216,12 +216,21 @@ return [ 'price_minor' => 0, 'max_branches' => 1, // Free: core patient records, appointments & consults, basic workflows only. + // One branch is created during onboarding; multi-branch management is Pro. + 'features' => [], ], 'pro' => [ 'label' => 'Pro', // Billed per active branch / month (unlimited branches). 'price_minor_per_branch' => (int) env('CARE_PRO_PRICE_PER_BRANCH_MINOR', env('CARE_PRO_PRICE_MINOR', 199000)), 'max_branches' => null, + 'features' => [ + 'branches', + 'lab', + 'pharmacy', + 'billing', + 'queue_integration', + ], ], 'enterprise' => [ 'label' => 'Enterprise', @@ -229,6 +238,13 @@ return [ // Differentiator: multi-dept custom workflows, priority support, analytics, AI. 'price_minor_per_branch' => (int) env('CARE_ENTERPRISE_PRICE_PER_BRANCH_MINOR', env('CARE_ENTERPRISE_PRICE_MINOR', 499000)), 'max_branches' => null, + 'features' => [ + 'branches', + 'lab', + 'pharmacy', + 'billing', + 'queue_integration', + ], ], ], diff --git a/resources/views/care/settings/edit.blade.php b/resources/views/care/settings/edit.blade.php index 2947d2c..459035c 100644 --- a/resources/views/care/settings/edit.blade.php +++ b/resources/views/care/settings/edit.blade.php @@ -49,7 +49,7 @@ @if ($canViewBranches || $canViewTeam) - +