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
@@ -5,11 +5,15 @@ namespace App\Dashboard\Controller;
use App\Auth\Entity\User;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Patient\Repository\PatientRecordRepository;
use App\Patient\Repository\PatientSessionRepository;
use App\Secretary\Repository\DoctorSecretaryRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Sms\Service\SmsWalletService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
@@ -21,24 +25,30 @@ class DashboardController extends BaseController
private readonly DoctorRepository $doctorRepo,
private readonly DoctorSecretaryRepository $secretaryRepo,
private readonly EntityManagerInterface $em,
private readonly SmsWalletService $smsWalletService,
private readonly PatientRecordRepository $patientRecordRepo,
private readonly PatientSessionRepository $patientSessionRepo,
) {}
// ── Clinic Dashboard ────────────────────────────────────────────────────
#[Route('/api/v1/dashboard/clinic', methods: ['GET'])]
#[IsGranted('ROLE_CLINIC')]
public function clinic(#[CurrentUser] User $user): JsonResponse
public function clinic(Request $request, #[CurrentUser] User $user): JsonResponse
{
$clinic = $this->clinicRepo->findByUser($user);
if ($clinic === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کلینیک یافت نشد', 404);
}
$clinicId = $clinic->getId();
$clinicId = $clinic->getId();
$todayStart = strtotime('today midnight');
$todayEnd = strtotime('tomorrow midnight') - 1;
$monthStart = strtotime('first day of this month midnight');
$from = $request->query->get('from') ? (int) $request->query->get('from') : $monthStart;
$to = $request->query->get('to') ? (int) $request->query->get('to') : time();
// آمار نوبت‌های امروز و این ماه
$stats = $this->em->createQuery('
SELECT
@@ -114,6 +124,10 @@ class DashboardController extends BaseController
'todayEnd' => $todayEnd,
])->getArrayResult();
$smsBalance = $this->smsWalletService->getBalance('clinic', $clinicId);
$uniquePatients = $this->patientRecordRepo->countUnique('clinic', $clinicId, $from, $to);
$revenuePeriod = $this->patientSessionRepo->sumRevenue('clinic', $clinicId, $from, $to);
return $this->success([
'clinic' => [
'uuid' => $clinic->getUuid(),
@@ -126,7 +140,11 @@ class DashboardController extends BaseController
'today_appointments' => (int) ($stats['today_appointments'] ?? 0),
'this_month_appointments' => $monthCount,
'pending_invitations' => $pendingInvitations,
'sms_wallet_balance' => $smsBalance,
'unique_patients_count' => $uniquePatients,
'revenue_period_rials' => $revenuePeriod,
],
'period' => ['from' => $from, 'to' => $to],
'today_appointments' => $todayAppts,
'doctors' => $doctors,
]);
@@ -136,7 +154,7 @@ class DashboardController extends BaseController
#[Route('/api/v1/dashboard/doctor', methods: ['GET'])]
#[IsGranted('ROLE_DOCTOR')]
public function doctor(#[CurrentUser] User $user): JsonResponse
public function doctor(Request $request, #[CurrentUser] User $user): JsonResponse
{
$doctor = $this->doctorRepo->findByUser($user);
if ($doctor === null) {
@@ -150,6 +168,9 @@ class DashboardController extends BaseController
$tmrEnd = strtotime('tomorrow midnight') + 86399;
$monthStart = strtotime('first day of this month midnight');
$from = $request->query->get('from') ? (int) $request->query->get('from') : $monthStart;
$to = $request->query->get('to') ? (int) $request->query->get('to') : time();
// آمار
$todayCount = (int) $this->em->createQuery('
SELECT COUNT(a.id) FROM App\Appointment\Entity\Appointment a
@@ -197,6 +218,10 @@ class DashboardController extends BaseController
WHERE d.id = :doctorId
')->setParameter('doctorId', $doctorId)->getArrayResult();
$smsBalance = $this->smsWalletService->getBalance('doctor', $doctorId);
$uniquePatients = $this->patientRecordRepo->countUnique('doctor', $doctorId, $from, $to);
$revenuePeriod = $this->patientSessionRepo->sumRevenue('doctor', $doctorId, $from, $to);
return $this->success([
'doctor' => [
'uuid' => $doctor->getUuid(),
@@ -209,7 +234,11 @@ class DashboardController extends BaseController
'this_month_appointments' => $monthCount,
'avg_rating' => $ratingRow['avg_score'] ? round((float) $ratingRow['avg_score'], 1) : null,
'total_ratings' => (int) ($ratingRow['total'] ?? 0),
'sms_wallet_balance' => $smsBalance,
'unique_patients_count' => $uniquePatients,
'revenue_period_rials' => $revenuePeriod,
],
'period' => ['from' => $from, 'to' => $to],
'today_appointments' => $todayAppts,
'clinics' => $clinics,
]);