feat: support multiple services per appointment (checkbox selection)

Appointments could only reference a single service (ManyToOne). Add an
appointment_service_items join table (ManyToMany) so an appointment can
carry several services; the first stays the primary service_item for
backward compatibility, and toArray now also returns service_items[].

Both create endpoints (my/appointment, admin/appointment) accept
service_item_uuids[] and attach all of them. A new duration_from_services
flag gates the slot_end recompute: service-booking mode sends it true
(slot_end = start + Σ durations); slot mode omits it so the manual end
time is preserved. The admin endpoint previously ignored services entirely.

Frontend: in slot mode the single service dropdown becomes a checkbox list
filtered by the selected section (multi-select); service mode sends the
duration flag. Migration + backend/entity tests + docs updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-16 10:10:47 +03:30
co-authored by Claude Opus 4.8
parent d76fd73529
commit 4642506c0f
9 changed files with 315 additions and 65 deletions
@@ -27,10 +27,13 @@ class PatientListNationalCodeTest extends ApiTestCase
{
[$owner, $doctor] = $this->doctor();
// db_test ریست نمی‌شود؛ کد ملیِ یکتا رندوم تا با اجراهای قبلی تصادم نکند.
$nc = '00' . str_pad((string) random_int(0, 99_999_999), 8, '0', STR_PAD_LEFT);
$patient = $this->createUser(['ROLE_USER']);
// کد ملی فقط روی پروفایل، نه روی خود کاربر
$profile = new UserProfile($patient);
$profile->setNationalCode('0012345675');
$profile->setNationalCode($nc);
$this->em->persist($profile);
$record = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId());
@@ -40,7 +43,7 @@ class PatientListNationalCodeTest extends ApiTestCase
$res = $this->authJson('GET', '/api/v1/patients', $owner);
self::assertSame(200, $this->responseCode());
self::assertSame($record->getUuid(), $res['data'][0]['uuid']);
self::assertSame('0012345675', $res['data'][0]['user_national_code']);
self::assertSame($nc, $res['data'][0]['user_national_code']);
}
public function testNullWhenNoNationalCodeAnywhere(): void