feat(subscription): implement effective plan logic and update free plan features

This commit is contained in:
hamed
2026-07-05 10:46:31 +03:30
parent f3ca6d844f
commit 828e3552c0
10 changed files with 148 additions and 30 deletions
@@ -61,12 +61,14 @@ class SubscriptionController extends BaseController
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
}
$subscription = $this->subscriptionService->getActiveSubscription($entityType, $entityId);
$usedTrial = $this->subscriptionService->hasUsedTrial($entityType, $entityId);
$subscription = $this->subscriptionService->getActiveSubscription($entityType, $entityId);
$usedTrial = $this->subscriptionService->hasUsedTrial($entityType, $entityId);
$effectivePlan = $this->subscriptionService->getEffectivePlan($entityType, $entityId);
return $this->success([
'subscription' => $subscription?->toArray(),
'used_trial' => $usedTrial,
'subscription' => $subscription?->toArray(),
'used_trial' => $usedTrial,
'effective_plan' => $effectivePlan?->toArray(),
]);
}
@@ -94,7 +96,7 @@ class SubscriptionController extends BaseController
#[IsGranted('ROLE_ADMIN')]
public function adminPlans(): JsonResponse
{
$plans = $this->planRepo->findAllActive();
$plans = $this->planRepo->findAllForAdmin();
return $this->paginated(
array_map(fn(SubscriptionPlan $p) => $p->toArray(withPeriods: true), $plans),
@@ -115,6 +117,10 @@ class SubscriptionController extends BaseController
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'name و level الزامی هستند', 422);
}
if ($this->planRepo->findByName($name) !== null) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'پلنی با این نام از قبل وجود دارد', 422);
}
$plan = new SubscriptionPlan(
$name,
(int) $data['level'],
@@ -138,7 +144,13 @@ class SubscriptionController extends BaseController
$data = json_decode($request->getContent(), true) ?? [];
if (isset($data['name'])) { $plan->setName($data['name']); }
if (isset($data['name'])) {
$existing = $this->planRepo->findByName(trim($data['name']));
if ($existing !== null && $existing->getUuid() !== $plan->getUuid()) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'پلنی با این نام از قبل وجود دارد', 422);
}
$plan->setName($data['name']);
}
if (isset($data['level'])) { $plan->setLevel((int) $data['level']); }
if (isset($data['max_secretaries'])) { $plan->setMaxSecretaries((int) $data['max_secretaries']); }
if (isset($data['features'])) { $plan->setFeatures($data['features']); }
+2 -2
View File
@@ -90,10 +90,10 @@ class SubscriptionPlan
];
if ($withPeriods) {
$data['periods'] = array_map(
$data['periods'] = array_values(array_map(
fn(SubscriptionPeriod $p) => $p->toArray(),
$this->periods->filter(fn(SubscriptionPeriod $p) => $p->isActive())->toArray()
);
));
}
return $data;
@@ -32,6 +32,14 @@ class SubscriptionPlanRepository extends ServiceEntityRepository
->getResult();
}
public function findAllForAdmin(): array
{
return $this->createQueryBuilder('p')
->orderBy('p.level', 'ASC')
->getQuery()
->getResult();
}
public function save(SubscriptionPlan $plan): void
{
$this->getEntityManager()->persist($plan);
@@ -7,6 +7,7 @@ use App\Payment\Entity\Payment;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\Subscription\Entity\ClinicSubscription;
use App\Subscription\Entity\SubscriptionPlan;
use App\Subscription\Repository\ClinicSubscriptionRepository;
use App\Subscription\Repository\SubscriptionPeriodRepository;
use App\Subscription\Repository\SubscriptionPlanRepository;
@@ -25,24 +26,28 @@ class SubscriptionService
return $this->subscriptionRepo->findActive($entityType, $entityId);
}
public function hasFeature(string $entityType, int $entityId, string $feature): bool
/**
* پلن مؤثر: پلن اشتراک فعال، یا در نبود اشتراک، پلن پیش‌فرض «free».
*/
public function getEffectivePlan(string $entityType, int $entityId): ?SubscriptionPlan
{
$subscription = $this->getActiveSubscription($entityType, $entityId);
if ($subscription === null) {
return false;
}
return $subscription->getPlan()->hasFeature($feature);
return $subscription?->getPlan() ?? $this->planRepo->findByName('free');
}
public function hasFeature(string $entityType, int $entityId, string $feature): bool
{
$plan = $this->getEffectivePlan($entityType, $entityId);
return $plan !== null && $plan->hasFeature($feature);
}
public function getSecretaryLimit(string $entityType, int $entityId): int
{
$subscription = $this->getActiveSubscription($entityType, $entityId);
if ($subscription === null) {
return 1;
}
$plan = $this->getEffectivePlan($entityType, $entityId);
return $subscription->getPlan()->getMaxSecretaries();
return $plan?->getMaxSecretaries() ?? 1;
}
public function hasUsedTrial(string $entityType, int $entityId): bool