feat: enhance staff management and payment gateway features
- Fix national code handling in staff creation and updates to support Persian digits. - Update ClinicStaff entity to allow longer national codes (up to 15 characters). - Implement support for clinic secretaries in SecretaryController, allowing creation without a doctor UUID. - Add a new endpoint to retrieve doctors associated with a clinic for secretary management. - Improve appointment management by ensuring doctors are selectable even when no appointments exist. - Extend PatientController to allow secretaries to create patient records if they have the appropriate permissions. - Introduce a PriceInput component for better price formatting in forms, supporting Persian digits. - Add a MockGateway for testing payment processes without real transactions. - Enhance SMS settings management with an approval flow for post-visit text messages, including new fields for pending text and status. - Update migrations to reflect changes in database schema for national codes and SMS settings.
This commit is contained in:
@@ -4,7 +4,9 @@ namespace App\Secretary\Controller;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Auth\Repository\UserRepository;
|
||||
use App\Clinic\Entity\Clinic;
|
||||
use App\Clinic\Repository\ClinicRepository;
|
||||
use App\Doctor\Entity\Doctor;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Secretary\Entity\DoctorSecretary;
|
||||
use App\Secretary\Repository\DoctorSecretaryRepository;
|
||||
@@ -46,8 +48,7 @@ class SecretaryController extends BaseController
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
// Only the doctor owner or admin can create secretary
|
||||
if ($doctor->getUser()->getId() !== $currentUser->getId() && !$currentUser->hasRole('ROLE_ADMIN')) {
|
||||
if (!$this->canManageDoctor($doctor, $currentUser)) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
@@ -164,7 +165,7 @@ class SecretaryController extends BaseController
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($doctor->getUser()->getId() !== $currentUser->getId() && !$currentUser->hasRole('ROLE_ADMIN')) {
|
||||
if (!$this->canManageDoctor($doctor, $currentUser)) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
@@ -176,9 +177,49 @@ class SecretaryController extends BaseController
|
||||
return $this->success(['data' => $secretaries]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/secretaries/clinic/{clinicUuid}', methods: ['GET'])]
|
||||
public function listByClinic(string $clinicUuid, #[CurrentUser] User $currentUser): JsonResponse
|
||||
{
|
||||
$clinic = $this->clinicRepo->findByUuid($clinicUuid);
|
||||
if ($clinic === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($clinic->getUser()->getId() !== $currentUser->getId() && !$currentUser->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$secretaries = array_map(
|
||||
fn(DoctorSecretary $s) => $s->toArray(),
|
||||
$this->secretaryRepo->findByClinic($clinic)
|
||||
);
|
||||
|
||||
return $this->success(['data' => $secretaries]);
|
||||
}
|
||||
|
||||
private function canManage(DoctorSecretary $secretary, User $user): bool
|
||||
{
|
||||
return $secretary->getDoctor()->getUser()->getId() === $user->getId()
|
||||
|| $user->hasRole('ROLE_ADMIN');
|
||||
return $this->canManageDoctor($secretary->getDoctor(), $user);
|
||||
}
|
||||
|
||||
private function canManageDoctor(Doctor $doctor, User $user): bool
|
||||
{
|
||||
if ($user->hasRole('ROLE_ADMIN')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($doctor->getUser()->getId() === $user->getId()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clinic owner can manage secretaries of its own doctors
|
||||
if ($user->hasRole('ROLE_CLINIC')) {
|
||||
$clinic = $this->clinicRepo->findByUser($user);
|
||||
if ($clinic !== null && $this->secretaryRepo->isDoctorInClinic($doctor, $clinic)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,20 +97,14 @@ class DoctorSecretary
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'user' => [
|
||||
'uuid' => $this->secretary->getUuid(),
|
||||
'realname' => $this->secretary->getRealName(),
|
||||
'mobile' => $this->secretary->getMobileNumber(),
|
||||
'picture' => null,
|
||||
],
|
||||
'doctor' => [
|
||||
'uuid' => $this->doctor->getUuid(),
|
||||
'name' => $this->doctor->getName(),
|
||||
],
|
||||
'active' => $this->active,
|
||||
'permissions' => $this->getPermissions(),
|
||||
'created_at' => $this->createdAt,
|
||||
'uuid' => $this->uuid,
|
||||
'user_name' => $this->secretary->getRealName(),
|
||||
'mobile_number' => $this->secretary->getMobileNumber(),
|
||||
'doctor_name' => $this->doctor->getName(),
|
||||
'doctor_uuid' => $this->doctor->getUuid(),
|
||||
'is_active' => $this->active,
|
||||
'permissions' => $this->getPermissions(),
|
||||
'created_at' => $this->createdAt,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Secretary\Repository;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Clinic\Entity\Clinic;
|
||||
use App\Doctor\Entity\Doctor;
|
||||
use App\Secretary\Entity\DoctorSecretary;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
@@ -37,6 +38,28 @@ class DoctorSecretaryRepository extends ServiceEntityRepository
|
||||
return $this->findBy(['doctor' => $doctor], ['createdAt' => 'DESC']);
|
||||
}
|
||||
|
||||
public function isDoctorInClinic(Doctor $doctor, Clinic $clinic): bool
|
||||
{
|
||||
return $clinic->getDoctors()->contains($doctor);
|
||||
}
|
||||
|
||||
/** @return DoctorSecretary[] — all secretaries across all doctors of a clinic */
|
||||
public function findByClinic(Clinic $clinic): array
|
||||
{
|
||||
$doctorIds = $clinic->getDoctors()->map(fn(Doctor $d) => $d->getId())->toArray();
|
||||
if (empty($doctorIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->createQueryBuilder('s')
|
||||
->join('s.doctor', 'd')
|
||||
->where('d.id IN (:ids)')
|
||||
->setParameter('ids', $doctorIds)
|
||||
->orderBy('s.createdAt', 'DESC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function findActiveBySecretary(User $user): ?DoctorSecretary
|
||||
{
|
||||
return $this->findOneBy(['secretary' => $user, 'active' => true]);
|
||||
|
||||
Reference in New Issue
Block a user