feat: implement staff management and subscription system

- Added StaffController for managing clinic staff, including listing, creating, updating, and toggling staff status.
- Created ClinicStaff entity and repository for staff data handling.
- Developed SubscriptionController to manage subscription plans and periods, including trial subscriptions.
- Introduced SubscriptionPlan, SubscriptionPeriod, and ClinicSubscription entities for subscription management.
- Implemented SubscriptionService for handling subscription logic, including trial activation and subscription creation from payments.
- Added necessary repositories for subscription entities to facilitate data access and manipulation.
This commit is contained in:
hamed
2026-06-14 22:10:28 +03:30
parent dcd631f503
commit b0244f28f5
53 changed files with 4434 additions and 40 deletions
@@ -0,0 +1,123 @@
<?php
namespace App\Subscription\Service;
use App\Config\Repository\SiteConfigRepository;
use App\Payment\Entity\Payment;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\Subscription\Entity\ClinicSubscription;
use App\Subscription\Repository\ClinicSubscriptionRepository;
use App\Subscription\Repository\SubscriptionPeriodRepository;
use App\Subscription\Repository\SubscriptionPlanRepository;
class SubscriptionService
{
public function __construct(
private readonly ClinicSubscriptionRepository $subscriptionRepo,
private readonly SubscriptionPlanRepository $planRepo,
private readonly SubscriptionPeriodRepository $periodRepo,
private readonly SiteConfigRepository $configRepo,
) {}
public function getActiveSubscription(string $entityType, int $entityId): ?ClinicSubscription
{
return $this->subscriptionRepo->findActive($entityType, $entityId);
}
public function hasFeature(string $entityType, int $entityId, string $feature): bool
{
$subscription = $this->getActiveSubscription($entityType, $entityId);
if ($subscription === null) {
return false;
}
return $subscription->getPlan()->hasFeature($feature);
}
public function getSecretaryLimit(string $entityType, int $entityId): int
{
$subscription = $this->getActiveSubscription($entityType, $entityId);
if ($subscription === null) {
return 1;
}
return $subscription->getPlan()->getMaxSecretaries();
}
public function hasUsedTrial(string $entityType, int $entityId): bool
{
return $this->subscriptionRepo->hasUsedTrial($entityType, $entityId);
}
public function activateTrial(string $entityType, int $entityId): ClinicSubscription
{
if ($this->hasUsedTrial($entityType, $entityId)) {
throw new AppException(ErrorCodes::ERR_TRIAL_ALREADY_USED, null, 422);
}
$trialEnabled = $this->configRepo->get('trial_enabled');
if ($trialEnabled === '0') {
throw new AppException(ErrorCodes::ERR_TRIAL_DISABLED, null, 422);
}
$basicPlan = $this->planRepo->findByName('basic');
if ($basicPlan === null) {
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, null, 500);
}
$trialPeriod = $this->periodRepo->findTrialPeriodForPlan($basicPlan);
if ($trialPeriod === null) {
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, null, 500);
}
$expiresAt = $this->calculateExpiresAt(null, $trialPeriod->getDurationMonths());
$subscription = new ClinicSubscription(
$entityType,
$entityId,
$basicPlan,
$trialPeriod,
true,
$expiresAt,
null
);
$this->subscriptionRepo->save($subscription);
return $subscription;
}
public function createFromPayment(Payment $payment, string $entityType, int $entityId, string $periodUuid): ClinicSubscription
{
$period = $this->periodRepo->findByUuid($periodUuid);
if ($period === null) {
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, null, 404);
}
$currentSub = $this->getActiveSubscription($entityType, $entityId);
$currentExpires = $currentSub?->getExpiresAt();
$expiresAt = $this->calculateExpiresAt($currentExpires, $period->getDurationMonths());
$subscription = new ClinicSubscription(
$entityType,
$entityId,
$period->getPlan(),
$period,
false,
$expiresAt,
$payment
);
$this->subscriptionRepo->save($subscription);
return $subscription;
}
public function calculateExpiresAt(?int $currentExpiresAt, int $durationMonths): int
{
$base = max($currentExpiresAt ?? 0, time());
return $base + $durationMonths * 30 * 86400;
}
}