feat(appointment): update patient identification to use national code from profile and enhance appointment creation logic
This commit is contained in:
@@ -882,6 +882,8 @@ class AdminApiController extends BaseController
|
||||
|
||||
$appointment = new Appointment($doctor, $patient, $slotStart, $slotEnd);
|
||||
$appointment->setPatientNationalCode($nationalCode);
|
||||
$appointment->setPatientName($patientName);
|
||||
$appointment->setPatientMobile($mobile);
|
||||
if (!empty($data['note'])) $appointment->setNote($data['note']);
|
||||
$locationId = $this->slotCalculator->resolveSlotLocationId($doctor, $slotStart);
|
||||
if ($locationId !== null) $appointment->setAddressId($locationId);
|
||||
|
||||
@@ -41,6 +41,7 @@ class MyAppointmentsController extends BaseController
|
||||
private readonly \App\Staff\Repository\ClinicStaffRepository $staffRepo,
|
||||
private readonly PatientResolver $patientResolver,
|
||||
private readonly \App\Auth\Repository\UserRepository $userRepo,
|
||||
private readonly \App\UserProfile\Repository\UserProfileRepository $profileRepo,
|
||||
) {}
|
||||
|
||||
#[Route('/api/v1/my/appointment', methods: ['POST'])]
|
||||
@@ -123,6 +124,7 @@ class MyAppointmentsController extends BaseController
|
||||
$appointment->setDepositAmountRials((int) $data['deposit_amount_rials']);
|
||||
}
|
||||
$appointment->setPatientName($patientName);
|
||||
$appointment->setPatientMobile($mobile);
|
||||
|
||||
if ($isReserve) {
|
||||
// Day-level reserve: no slot occupation, plain save (no atomic slot check).
|
||||
@@ -170,11 +172,14 @@ class MyAppointmentsController extends BaseController
|
||||
return $this->success(['found' => false]);
|
||||
}
|
||||
|
||||
// National code lives on the profile (profiles.national_code), not on User.
|
||||
$nationalCode = $this->profileRepo->findByUser($patient)?->getNationalCode();
|
||||
|
||||
return $this->success([
|
||||
'found' => true,
|
||||
'name' => $patient->getRealName(),
|
||||
'mobile' => $patient->getMobileNumber(),
|
||||
'national_code' => $patient->getNationalCode(),
|
||||
'national_code' => $nationalCode,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,6 @@ class UserRepository extends ServiceEntityRepository
|
||||
return $this->findOneBy(['mobileNumber' => $mobile]);
|
||||
}
|
||||
|
||||
public function findByNationalCode(string $nationalCode): ?User
|
||||
{
|
||||
return $this->findOneBy(['nationalCode' => $nationalCode]);
|
||||
}
|
||||
|
||||
public function findByUuid(string $uuid): ?User
|
||||
{
|
||||
return $this->findOneBy(['uuid' => $uuid]);
|
||||
|
||||
@@ -6,53 +6,71 @@ use App\Auth\Entity\User;
|
||||
use App\Auth\Repository\UserRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Exception\AppException;
|
||||
use App\UserProfile\Entity\UserProfile;
|
||||
use App\UserProfile\Repository\UserProfileRepository;
|
||||
|
||||
/**
|
||||
* Resolves (or creates) the patient User for an admin-side booking.
|
||||
*
|
||||
* Identity key is the national code, which is unique per person. Mobile is only
|
||||
* a contact detail — one national code may be booked under several mobiles — so
|
||||
* Identity key is the national code, which is stored on the patient's
|
||||
* UserProfile (unique per person — profiles.national_code). Mobile is only a
|
||||
* contact detail; one national code may be booked under several mobiles, so
|
||||
* lookup prefers the national code and never overwrites an existing mobile.
|
||||
* Keeping resolution here (not duplicated in each controller) keeps the case-file
|
||||
* (PatientRecord, keyed on user_id) unique per national code.
|
||||
* Keeping resolution here (not duplicated in each controller) keeps the
|
||||
* case-file (PatientRecord, keyed on user_id) unique per national code.
|
||||
*/
|
||||
class PatientResolver
|
||||
{
|
||||
public function __construct(private readonly UserRepository $userRepo) {}
|
||||
public function __construct(
|
||||
private readonly UserRepository $userRepo,
|
||||
private readonly UserProfileRepository $profileRepo,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $nationalCode already normalized to English digits and validated
|
||||
*/
|
||||
public function resolveForBooking(string $nationalCode, string $mobile, string $name): User
|
||||
{
|
||||
$user = $this->userRepo->findByNationalCode($nationalCode);
|
||||
if ($user !== null) {
|
||||
// 1) National code lives on the profile and is unique — the real identity.
|
||||
$profile = $this->profileRepo->findOneByNationalCode($nationalCode);
|
||||
if ($profile !== null) {
|
||||
$user = $profile->getUser();
|
||||
$this->fillNameIfEmpty($user, $name);
|
||||
return $user;
|
||||
}
|
||||
|
||||
// 2) Fall back to mobile; bind the national code to that patient's profile.
|
||||
$user = $this->userRepo->findByMobile($mobile);
|
||||
if ($user !== null) {
|
||||
$existing = $user->getNationalCode();
|
||||
$profile = $this->profileRepo->findByUser($user);
|
||||
$existing = $profile?->getNationalCode();
|
||||
if ($existing !== null && $existing !== $nationalCode) {
|
||||
throw new AppException(ErrorCodes::ERR_PROFILE_MOBILE_TAKEN, 'این شماره موبایل با کد ملی دیگری ثبت شده است', 422, 'patient_mobile');
|
||||
}
|
||||
if ($existing === null) {
|
||||
$user->setNationalCode($nationalCode);
|
||||
$this->bindNationalCode($profile ?? new UserProfile($user), $nationalCode);
|
||||
}
|
||||
$this->fillNameIfEmpty($user, $name);
|
||||
return $user;
|
||||
}
|
||||
|
||||
// 3) New patient: create the user and its profile carrying the national code.
|
||||
$user = new User($mobile);
|
||||
$user->setRealName($name);
|
||||
$user->setNationalCode($nationalCode);
|
||||
$user->setRoles(['ROLE_USER']);
|
||||
$this->userRepo->save($user, false);
|
||||
|
||||
$this->bindNationalCode(new UserProfile($user), $nationalCode);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function bindNationalCode(UserProfile $profile, string $nationalCode): void
|
||||
{
|
||||
$profile->setNationalCode($nationalCode);
|
||||
$this->profileRepo->save($profile, false);
|
||||
}
|
||||
|
||||
private function fillNameIfEmpty(User $user, string $name): void
|
||||
{
|
||||
if (($user->getRealName() ?? '') === '' && $name !== '') {
|
||||
|
||||
Reference in New Issue
Block a user