feat(appointment): update patient identification to use national code from profile and enhance appointment creation logic

This commit is contained in:
hamed
2026-07-15 18:46:56 +03:30
parent 0dc1ab949a
commit de6fe1bd56
8 changed files with 94 additions and 32 deletions
@@ -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,
]);
}
-5
View File
@@ -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]);
+28 -10
View File
@@ -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 !== '') {