diff --git a/assets/admin/components/resources/ResourceFormModal.tsx b/assets/admin/components/resources/ResourceFormModal.tsx index c154a74e..eb896997 100644 --- a/assets/admin/components/resources/ResourceFormModal.tsx +++ b/assets/admin/components/resources/ResourceFormModal.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useState } from 'react'; import Modal from '../ui/Modal'; +import { useQuery } from '@tanstack/react-query'; import SearchableSelect from '../ui/SearchableSelect'; +import { api, type ApiResponse } from '../../lib/api'; import type { Branch, ClinicResource, ResourcePayload, ResourceType } from '../../types'; import { useResourceDetail } from '../../hooks/useResources'; import { formatNumber } from '../../lib/utils'; @@ -25,9 +27,21 @@ export default function ResourceFormModal({ }: Props) { // شمار نوبت‌های آینده فقط برای منبعِ موجود معنا دارد و فقط وقتی مودال باز است. const { upcomingAppointments: upcoming } = useResourceDetail(open ? resource?.uuid : undefined); + // پزشکان محیط جاری برای انتخاب ناظر — همان اندپوینت احرازشده‌ای که صفحهٔ نوبت‌ها + // استفاده می‌کند، تا منشی فقط پزشکان مجازش را ببیند. + const doctorsQuery = useQuery>({ + queryKey: ['booking-doctors'], + queryFn: () => api.get('/api/v1/my/clinic-doctors'), + enabled: open, + staleTime: 60_000, + }); + const doctors = doctorsQuery.data?.data?.data ?? []; + const doctorsLoading = doctorsQuery.isLoading; + const [name, setName] = useState(''); const [addressUuid, setAddressUuid] = useState(null); const [typeUuid, setTypeUuid] = useState(null); + const [supervisorUuid, setSupervisorUuid] = useState(null); const [capacity, setCapacity] = useState('1'); const [setupMinutes, setSetupMinutes] = useState('0'); const [cleanupMinutes, setCleanupMinutes] = useState('0'); @@ -39,6 +53,7 @@ export default function ResourceFormModal({ setName(resource?.name ?? ''); setAddressUuid(resource?.address_uuid ?? null); setTypeUuid(resource?.type_uuid ?? null); + setSupervisorUuid(resource?.supervisor?.uuid ?? null); setCapacity(String(resource?.capacity ?? 1)); setSetupMinutes(String(resource?.setup_minutes ?? 0)); setCleanupMinutes(String(resource?.cleanup_minutes ?? 0)); @@ -52,6 +67,7 @@ export default function ResourceFormModal({ const parsedCapacity = Number(capacity); const invalid = name.trim() === '' || + !supervisorUuid || (!isEdit && (!addressUuid || !typeUuid)) || !Number.isFinite(parsedCapacity) || parsedCapacity < 1; @@ -69,6 +85,7 @@ export default function ResourceFormModal({ cleanup_minutes: Number(cleanupMinutes) || 0, attributes: attrs, active, + supervisor_doctor_uuid: supervisorUuid!, }; // شعبه و نوع فقط هنگام ساخت فرستاده می‌شوند؛ جفت محیطِ منبع از آدرس مشتق شده و @@ -109,6 +126,18 @@ export default function ResourceFormModal({ height={38} /> + {/* ناظر برخلاف شعبه و نوع در ویرایش هم قابل تغییر است: پزشکِ مسئولِ یک + دستگاه عوض می‌شود، ولی محیطِ منبع نه. */} + + ({ value: d.uuid, label: d.name }))} + value={supervisorUuid} + onChange={(v) => setSupervisorUuid(v ? String(v) : null)} + placeholder="پزشک ناظر را انتخاب کنید" + isLoading={doctorsLoading} + height={38} + /> + {isEdit && ( diff --git a/assets/admin/types/index.ts b/assets/admin/types/index.ts index e47b4729..b588ebfc 100644 --- a/assets/admin/types/index.ts +++ b/assets/admin/types/index.ts @@ -989,6 +989,8 @@ export interface ClinicResource { setup_minutes: number; cleanup_minutes: number; attributes: Record; + /** پزشکِ ناظر — الزامی. جدا از پل: دستگاهِ تحت نظارت، «منبع انسانی» نمی‌شود. */ + supervisor: { uuid: string; name: string } | null; /** `null` یعنی دستگاه/تجهیزات — منبعی که پل به موجودیت دیگری ندارد */ subject_kind: 'doctor' | 'staff' | 'room' | null; subject_uuid: string | null; @@ -1002,6 +1004,7 @@ export interface ClinicResource { export interface ResourcePayload { name: string; + supervisor_doctor_uuid?: string; address_uuid?: string; type_uuid?: string; capacity?: number; diff --git a/docs/api/resource.md b/docs/api/resource.md index 3f692f13..eb4e4181 100644 --- a/docs/api/resource.md +++ b/docs/api/resource.md @@ -178,6 +178,7 @@ |---|---|---|---| | `address_uuid` | string | ✅ | محل نوبت‌دهی؛ جفت محیطِ منبع **از همین** مشتق می‌شود، نه از بدنه | | `type_uuid` | string | ✅ | | +| `supervisor_doctor_uuid` | string | ✅ | پزشکِ ناظرِ منبع. باید پزشکِ همین محیط باشد وگرنه ۴۰۴ | | `name` | string | ✅ | حداکثر ۱۵۰ نویسه | | `capacity` | int | — | پیش‌فرض ۱، حداقل ۱؛ روی منبعِ شخص حداکثر ۱ | | `setup_minutes` | int | — | پیش‌فرض ۰، بازهٔ ۰..۴۸۰ | @@ -185,6 +186,36 @@ | `attributes` | object | — | حداکثر ۲۰ کلید · کلید `[a-z_]{1,40}` · مقدار فقط اسکالر | | `active` | bool | — | پیش‌فرض `true` | +### پزشک ناظر (2026-08) + +هر منبع **الزاماً** زیر نظر یک پزشک است؛ صفحهٔ نوبت‌ها تب منابع را زیر همان پزشک +می‌چیند، پس منبعِ بی‌ناظر جایی برای دیده‌شدن ندارد. + +- `supervisor_doctor_uuid` در ساخت الزامی است → نبودش `422` با + `field: supervisor_doctor_uuid` و پیام «انتخاب پزشک ناظر الزامی است». +- در `PATCH` **اختیاری** است، ولی اگر بیاید نمی‌تواند خالی باشد (برداشتن ناظر ممنوع). +- پزشکِ خارج از محیط → `404` («پزشک ناظر یافت نشد») — وجود دادهٔ محیط بیگانه لو نمی‌رود. +- پاسخ‌ها فیلد `supervisor` را به شکل `{uuid, name}` برمی‌گردانند (یا `null` برای + ردیف‌هایی که پزشکشان حذف شده — کلید خارجی `SET NULL` است). + +**ناظر با پلِ منبع فرق دارد.** `subject_kind`/`doctor_id` یعنی «این منبع خودِ همان +پزشک است» و `isPerson()` بر پایه‌اش ظرفیت را به ۱ قفل می‌کند. ناظرِ یک دستگاهِ +سه‌ظرفیتی نباید آن را به منبعِ انسانی تبدیل کند، پس ستون جداست. + +خروجی واقعی `GET /api/v1/resources?active=1`: + +```json +{ + "name": "اتاق ۱", + "supervisor": { "uuid": "631e81d8-0009-4e01-a40f-3029905a3f27", "name": "امیر کاظمی" }, + "subject_kind": null, + "capacity": 1 +} +``` + +**backfill:** ۱۳ منبعِ موجود در migration ناظر گرفتند — منبعِ مطب → همان پزشک، منبعِ +کلینیک → پزشکِ اولِ همان کلینیک. قابل تغییر از فرم ویرایش منبع. + `attributes` عمداً آزاد است — کلید ناشناخته پذیرفته می‌شود — ولی مقدارش باید ساده باشد. دلیل: تسک ۰۵ قید `same_gender` و تسک ۰۹ شرط‌های منبع را با مقایسهٔ ساده روی همین مقادیر می‌سنجند؛ آرایهٔ تودرتو یعنی مقایسهٔ دلخواه، همان چیزی که بند ۸ مستند diff --git a/migrations/Version20260803093000.php b/migrations/Version20260803093000.php new file mode 100644 index 00000000..3e380e68 --- /dev/null +++ b/migrations/Version20260803093000.php @@ -0,0 +1,59 @@ +addSql('ALTER TABLE clinic_resources ADD supervisor_id INT DEFAULT NULL'); + $this->addSql( + 'ALTER TABLE clinic_resources ' + . 'ADD CONSTRAINT FK_resource_supervisor FOREIGN KEY (supervisor_id) ' + . 'REFERENCES doctors (id) ON DELETE SET NULL' + ); + $this->addSql('CREATE INDEX idx_resource_supervisor ON clinic_resources (supervisor_id)'); + + // منبعِ یک مطب شخصی: ناظرش خودِ همان پزشک است. + $this->addSql( + 'UPDATE clinic_resources SET supervisor_id = entity_id ' + . "WHERE supervisor_id IS NULL AND entity_type = 'doctor'" + ); + + // منبعِ کلینیک: پزشکِ اولِ همان کلینیک. اپراتور می‌تواند بعداً عوضش کند. + $this->addSql( + 'UPDATE clinic_resources r SET r.supervisor_id = (' + . ' SELECT MIN(cd.doctor_id) FROM clinic_doctors cd WHERE cd.clinic_id = r.entity_id' + . ") WHERE r.supervisor_id IS NULL AND r.entity_type = 'clinic'" + ); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP INDEX idx_resource_supervisor ON clinic_resources'); + $this->addSql('ALTER TABLE clinic_resources DROP FOREIGN KEY FK_resource_supervisor'); + $this->addSql('ALTER TABLE clinic_resources DROP supervisor_id'); + } +} diff --git a/src/Resource/Controller/ResourceController.php b/src/Resource/Controller/ResourceController.php index bd8a6f0f..47fab478 100644 --- a/src/Resource/Controller/ResourceController.php +++ b/src/Resource/Controller/ResourceController.php @@ -81,10 +81,17 @@ class ResourceController extends BaseController return $this->error(ErrorCodes::ERR_VALIDATION_002, 'فیلد type_uuid الزامی است', 422, 'type_uuid'); } - $address = $this->context->address($user, $data['address_uuid']); - $type = $this->context->type($user, $data['type_uuid']); + // هر منبع زیر نظر یک پزشک است — صفحهٔ نوبت‌ها تب منابع را زیر همان پزشک می‌چیند، + // پس منبعِ بی‌ناظر جایی برای دیده‌شدن ندارد. + if (!is_string($data['supervisor_doctor_uuid'] ?? null)) { + return $this->error(ErrorCodes::ERR_VALIDATION_002, 'انتخاب پزشک ناظر الزامی است', 422, 'supervisor_doctor_uuid'); + } - return $this->success($this->service->create($address, $type, $data)->toArray(), 201); + $address = $this->context->address($user, $data['address_uuid']); + $type = $this->context->type($user, $data['type_uuid']); + $supervisor = $this->context->supervisor($user, $data['supervisor_doctor_uuid']); + + return $this->success($this->service->create($address, $type, $data, $supervisor)->toArray(), 201); } #[Route('/api/v1/resource/{uuid}', name: 'resource_show', methods: ['GET'])] @@ -118,6 +125,15 @@ class ResourceController extends BaseController $resource->setType($this->context->type($user, $data['type_uuid'])); } + // ناظر عوض می‌شود ولی برداشته نمی‌شود: فرستادن رشتهٔ خالی یعنی منبعِ بی‌ناظر. + if (array_key_exists('supervisor_doctor_uuid', $data)) { + if (!is_string($data['supervisor_doctor_uuid']) || $data['supervisor_doctor_uuid'] === '') { + return $this->error(ErrorCodes::ERR_VALIDATION_002, 'انتخاب پزشک ناظر الزامی است', 422, 'supervisor_doctor_uuid'); + } + + $resource->setSupervisor($this->context->supervisor($user, $data['supervisor_doctor_uuid'])); + } + return $this->success($this->service->update($resource, $data)->toArray()); } diff --git a/src/Resource/Entity/ClinicResource.php b/src/Resource/Entity/ClinicResource.php index 5eec6735..662eede5 100644 --- a/src/Resource/Entity/ClinicResource.php +++ b/src/Resource/Entity/ClinicResource.php @@ -84,6 +84,20 @@ class ClinicResource #[ORM\JoinColumn(name: 'staff_id', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] private ?ClinicStaff $staff = null; + /** + * پزشکِ ناظرِ منبع — کسی که این دستگاه/اتاق زیر نظر او کار می‌کند. + * + * عمداً از `$doctor` جداست: آن **پل** است («این منبع خودِ همان پزشک است») و + * `isPerson()` بر پایه‌اش ظرفیت را به ۱ قفل می‌کند. ناظرِ یک دستگاهِ سه‌ظرفیتی + * نباید آن را به منبعِ انسانی تبدیل کند. + * + * `SET NULL` نه `CASCADE`: حذف پزشک نباید دستگاه کلینیک را پاک کند. ستون در + * دیتابیس تهی‌پذیر است تا حذف پزشک ردیف را نشکند، ولی API ناظر را الزامی می‌گیرد. + */ + #[ORM\ManyToOne(targetEntity: Doctor::class)] + #[ORM\JoinColumn(name: 'supervisor_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')] + private ?Doctor $supervisor = null; + #[ORM\Column(type: 'boolean', options: ['default' => true])] private bool $active = true; @@ -183,6 +197,16 @@ class ClinicResource return $this; } + public function getSupervisor(): ?Doctor { return $this->supervisor; } + + public function setSupervisor(?Doctor $doctor): self + { + $this->supervisor = $doctor; + $this->touch(); + + return $this; + } + /** منبعی که یک انسان است: ظرفیتش همیشه ۱ می‌ماند. */ public function isPerson(): bool { @@ -246,6 +270,9 @@ class ClinicResource 'setup_minutes' => $this->setupMinutes, 'cleanup_minutes' => $this->cleanupMinutes, 'attributes' => (object) $this->getAttributes(), + 'supervisor' => $this->supervisor === null + ? null + : ['uuid' => $this->supervisor->getUuid(), 'name' => $this->supervisor->getName()], 'subject_kind' => match (true) { $this->doctor !== null => 'doctor', $this->staff !== null => 'staff', diff --git a/src/Resource/Service/ResourceContext.php b/src/Resource/Service/ResourceContext.php index c71f052f..eed3918c 100644 --- a/src/Resource/Service/ResourceContext.php +++ b/src/Resource/Service/ResourceContext.php @@ -4,13 +4,16 @@ namespace App\Resource\Service; use App\Auth\Entity\User; use App\Doctor\Service\AddressResolver; +use App\Doctor\Entity\Doctor; use App\Doctor\Entity\DoctorAddress; +use App\Doctor\Repository\DoctorRepository; use App\Resource\Entity\ClinicResource; use App\Resource\Entity\ResourcePool; use App\Resource\Entity\ResourceType; use App\Resource\Entity\Skill; use App\Resource\Repository\ClinicResourceRepository; use App\Resource\Repository\ResourcePoolRepository; +use App\Clinic\Repository\ClinicRepository; use App\Resource\Repository\ResourceTypeRepository; use App\Resource\Repository\SkillRepository; use App\Shared\Constant\ErrorCodes; @@ -36,6 +39,8 @@ final class ResourceContext private readonly SkillRepository $skills, private readonly ResourcePoolRepository $pools, private readonly TenantOwnershipChecker $ownership, + private readonly DoctorRepository $doctors, + private readonly ClinicRepository $clinics, ) {} /** @return array{0: string, 1: int} */ @@ -54,6 +59,38 @@ final class ResourceContext return $this->owned($user, $this->types->findByUuid($uuid), 'نوع منبع یافت نشد'); } + /** + * پزشکِ ناظرِ منبع. + * + * `Doctor` تحت `TenantOwnedTrait` نیست (پزشک می‌تواند هم‌زمان عضو چند کلینیک باشد)، + * پس مالکیت با عضویت سنجیده می‌شود: در محیط کلینیک باید پزشکِ همان کلینیک باشد، و در + * مطب شخصی باید خودِ همان پزشک. بدون این، منشیِ یک کلینیک می‌توانست دستگاهش را زیر + * نظر پزشک کلینیک دیگری ببرد. + */ + public function supervisor(User $user, string $uuid): Doctor + { + $doctor = $this->doctors->findByUuid($uuid); + + if ($doctor === null) { + throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک ناظر یافت نشد', 404); + } + + [$entityType, $entityId] = $this->pair($user); + + // رابطه از سمت کلینیک تعریف شده (`Clinic::$doctors`)، پس عضویت را خودِ کلینیک + // جواب می‌دهد — `Clinic::hasDoctor()`؛ کوئری تازه‌ای لازم نیست. + $clinic = $entityType === 'clinic' ? $this->clinics->find($entityId) : null; + $belongs = $entityType === 'clinic' + ? ($clinic !== null && $clinic->hasDoctor($doctor)) + : (int) $doctor->getId() === $entityId; + + if (!$belongs) { + throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک ناظر یافت نشد', 404); + } + + return $doctor; + } + public function resource(User $user, string $uuid): ClinicResource { return $this->owned($user, $this->resources->findByUuid($uuid), 'منبع یافت نشد'); diff --git a/src/Resource/Service/ResourceService.php b/src/Resource/Service/ResourceService.php index 02fabfad..5349ed03 100644 --- a/src/Resource/Service/ResourceService.php +++ b/src/Resource/Service/ResourceService.php @@ -2,6 +2,7 @@ namespace App\Resource\Service; +use App\Doctor\Entity\Doctor; use App\Doctor\Entity\DoctorAddress; use App\Resource\Entity\ClinicResource; use App\Resource\Entity\ResourceType; @@ -57,9 +58,10 @@ final class ResourceService } /** @param array $data */ - public function create(DoctorAddress $address, ResourceType $type, array $data): ClinicResource + public function create(DoctorAddress $address, ResourceType $type, array $data, Doctor $supervisor): ClinicResource { $resource = new ClinicResource($address, $type, $this->assertName($data['name'] ?? null)); + $resource->setSupervisor($supervisor); $this->applyOptional($resource, $data); $this->em->persist($resource); diff --git a/tests/Resource/ResourceCrudTest.php b/tests/Resource/ResourceCrudTest.php index 8ba72281..03db8d55 100644 --- a/tests/Resource/ResourceCrudTest.php +++ b/tests/Resource/ResourceCrudTest.php @@ -111,15 +111,90 @@ class ResourceCrudTest extends ResourceTestCase [, , $foreignAddress] = $this->clinicWithAddress('شعبهٔ بیگانه'); + // ناظر معتبرِ محیط خودی می‌آید تا خطای آدرسِ بیگانه سنجیده شود، نه خطای فیلد کم. $this->authJson('POST', '/api/v1/resource', $user, [ - 'address_uuid' => $foreignAddress->getUuid(), - 'type_uuid' => $type->getUuid(), - 'name' => 'دستگاه', + 'address_uuid' => $foreignAddress->getUuid(), + 'type_uuid' => $type->getUuid(), + 'name' => 'دستگاه', + 'supervisor_doctor_uuid' => $this->supervisorFor($address)->getUuid(), ]); self::assertSame(404, $this->responseCode()); } + /** + * هر منبع زیر نظر یک پزشک است — صفحهٔ نوبت‌ها تب منابع را زیر همان پزشک می‌چیند، + * پس منبعِ بی‌ناظر جایی برای دیده‌شدن ندارد. + */ + public function testSupervisorIsRequiredOnCreate(): void + { + [$user, , $address] = $this->clinicWithAddress(); + + $body = $this->authJson('POST', '/api/v1/resource', $user, [ + 'address_uuid' => $address->getUuid(), + 'type_uuid' => $this->resourceType($address)->getUuid(), + 'name' => 'دستگاه بی‌ناظر', + ]); + + self::assertSame(422, $this->responseCode()); + self::assertSame('supervisor_doctor_uuid', $body['errors'][0]['field']); + } + + public function testSupervisorIsReturnedOnTheResource(): void + { + [$user, , $address] = $this->clinicWithAddress(); + $supervisor = $this->supervisorFor($address); + + $body = $this->createResource($user, $address, $this->resourceType($address)); + + self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); + self::assertSame($supervisor->getUuid(), $body['data']['supervisor']['uuid']); + self::assertSame($supervisor->getName(), $body['data']['supervisor']['name']); + } + + /** ناظر پلِ منبع نیست: دستگاه با ناظر نباید «منبع انسانی» شود و ظرفیتش قفل بماند. */ + public function testSupervisorDoesNotTurnADeviceIntoAPersonResource(): void + { + [$user, , $address] = $this->clinicWithAddress(); + + $body = $this->createResource($user, $address, $this->resourceType($address), ['capacity' => 3]); + + self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); + self::assertSame(3, $body['data']['capacity']); + self::assertNull($body['data']['subject_kind'], 'ناظر نباید پل شمرده شود'); + } + + /** منشیِ یک کلینیک نباید دستگاهش را زیر نظر پزشک کلینیک دیگری ببرد. */ + public function testForeignSupervisorIsNotFound(): void + { + [$user, , $address] = $this->clinicWithAddress(); + [, , $foreignAddress] = $this->clinicWithAddress('کلینیک بیگانه'); + $foreignDoctor = $this->supervisorFor($foreignAddress); + + $this->authJson('POST', '/api/v1/resource', $user, [ + 'address_uuid' => $address->getUuid(), + 'type_uuid' => $this->resourceType($address)->getUuid(), + 'name' => 'دستگاه', + 'supervisor_doctor_uuid' => $foreignDoctor->getUuid(), + ]); + + self::assertSame(404, $this->responseCode()); + } + + /** تغییر ناظر مجاز است، برداشتنش نه. */ + public function testSupervisorCannotBeCleared(): void + { + [$user, , $address] = $this->clinicWithAddress(); + $created = $this->createResource($user, $address, $this->resourceType($address)); + + $body = $this->authJson('PATCH', "/api/v1/resource/{$created['data']['uuid']}", $user, [ + 'supervisor_doctor_uuid' => '', + ]); + + self::assertSame(422, $this->responseCode()); + self::assertSame('supervisor_doctor_uuid', $body['errors'][0]['field']); + } + public function testForeignResourceIsNotFound(): void { [$ownerUser, , $ownerAddress] = $this->clinicWithAddress(); diff --git a/tests/Resource/ResourceTestCase.php b/tests/Resource/ResourceTestCase.php index ebb5d59c..5cb1fdb8 100644 --- a/tests/Resource/ResourceTestCase.php +++ b/tests/Resource/ResourceTestCase.php @@ -61,6 +61,34 @@ abstract class ResourceTestCase extends ApiTestCase return [$user, $doctor, $address]; } + /** + * پزشکِ ناظرِ محیطِ همین آدرس — از وقتی ناظر روی منبع الزامی شد، هر ساختِ منبع + * یکی لازم دارد. برای کلینیک یک پزشک عضو ساخته می‌شود و برای مطب، خودِ پزشک. + */ + protected function supervisorFor(DoctorAddress $address): Doctor + { + if ($address->tenantEntityType() === 'doctor') { + return $this->em->getRepository(Doctor::class)->find($address->tenantEntityId()); + } + + $clinic = $this->em->getRepository(Clinic::class)->find($address->tenantEntityId()); + + foreach ($clinic->getDoctors() as $existing) { + return $existing; + } + + $user = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']); + $doctor = new Doctor($user, 'پزشک ناظر'); + $doctor->setMobileNumber($user->getMobileNumber()); + $this->em->persist($doctor); + $this->em->flush(); + + $clinic->getDoctors()->add($doctor); + $this->em->flush(); + + return $doctor; + } + protected function resourceType(DoctorAddress $address, string $code = 'device', string $name = 'دستگاه'): ResourceType { $type = new ResourceType($address->tenantEntityType(), $address->tenantEntityId(), $code, $name); @@ -83,9 +111,10 @@ abstract class ResourceTestCase extends ApiTestCase protected function createResource(User $user, DoctorAddress $address, ResourceType $type, array $body = []): array { return $this->authJson('POST', '/api/v1/resource', $user, $body + [ - 'address_uuid' => $address->getUuid(), - 'type_uuid' => $type->getUuid(), - 'name' => 'لیزر آلکساندرایت ۱', + 'address_uuid' => $address->getUuid(), + 'type_uuid' => $type->getUuid(), + 'name' => 'لیزر آلکساندرایت ۱', + 'supervisor_doctor_uuid' => $this->supervisorFor($address)->getUuid(), ]); }