From 1543eddd9e962e5ec41f5e15c163c47049f1b635 Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Mon, 3 Aug 2026 13:47:39 +0330 Subject: [PATCH] feat(resource): expose each offering's service section The resource booking modal groups services under their section, the way the doctor's service booking does. The offering list had no section, so the list could only be flat. Co-Authored-By: Claude Opus 5 (1M context) --- docs/api/resource.md | 17 ++++++++++++++ .../ResourceServiceAssignmentService.php | 6 +++++ .../Resource/ResourceServiceOfferingTest.php | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/docs/api/resource.md b/docs/api/resource.md index eb4e4181..1247faf2 100644 --- a/docs/api/resource.md +++ b/docs/api/resource.md @@ -186,6 +186,23 @@ | `attributes` | object | — | حداکثر ۲۰ کلید · کلید `[a-z_]{1,40}` · مقدار فقط اسکالر | | `active` | bool | — | پیش‌فرض `true` | +### `service_section` روی فهرست سرویس‌های منبع (2026-08) + +`GET /api/v1/resource/{uuid}/services` برای هر سرویس `service_section` را هم می‌دهد +(`{uuid, name}`). مودالِ رزرو منبع سرویس‌ها را زیر بخششان گروه می‌کند — مثل نوبت‌دهی +سرویسیِ پزشک — و بدون این فیلد فهرست یک لیستِ تختِ بی‌دسته می‌شد. + +خروجی واقعی: + +```json +{ + "service_name": "لیزر زیربغل", + "service_section": { "uuid": "9eac29e4-…", "name": "خدمات لیزر و زیبایی" }, + "effective_duration_minutes": 20, + "active": true +} +``` + ### پزشک ناظر (2026-08) هر منبع **الزاماً** زیر نظر یک پزشک است؛ صفحهٔ نوبت‌ها تب منابع را زیر همان پزشک diff --git a/src/Resource/Service/ResourceServiceAssignmentService.php b/src/Resource/Service/ResourceServiceAssignmentService.php index f81374b6..c8f22401 100644 --- a/src/Resource/Service/ResourceServiceAssignmentService.php +++ b/src/Resource/Service/ResourceServiceAssignmentService.php @@ -47,6 +47,12 @@ final class ResourceServiceAssignmentService 'effective_price_rials' => $spec->priceRials, 'duration_source' => $spec->durationSource, 'price_source' => $spec->priceSource, + // مودالِ رزرو منبع سرویس‌ها را زیر بخششان گروه می‌کند؛ بدون این، فهرست + // یک لیست تختِ بی‌دسته می‌شد. + 'service_section' => [ + 'uuid' => $item->getSection()->getUuid(), + 'name' => $item->getSection()->getName(), + ], ]; } diff --git a/tests/Resource/ResourceServiceOfferingTest.php b/tests/Resource/ResourceServiceOfferingTest.php index 832f5f8d..daece268 100644 --- a/tests/Resource/ResourceServiceOfferingTest.php +++ b/tests/Resource/ResourceServiceOfferingTest.php @@ -159,4 +159,26 @@ class ResourceServiceOfferingTest extends ResourceTestCase self::assertSame([], $repo->activeResourceIdsFor($service)); self::assertSame(25, $repo->findOneFor($resource, $service)?->getDurationMinutes()); } + + /** مودال رزرو منبع سرویس‌ها را زیر بخششان گروه می‌کند، پس فهرست باید بخش را بدهد. */ + public function testOfferingListCarriesTheServiceSection(): void + { + [$user, , $address] = $this->clinicWithAddress(); + $resource = $this->resource($address, $this->resourceType($address), 'لیزر الکساندرایت'); + $service = $this->service($address, 'لیزر زیربغل'); + + $this->authJson('PUT', "/api/v1/resource/{$resource->getUuid()}/services", $user, [ + 'services' => [['service_uuid' => $service->getUuid()]], + ]); + self::assertSame(200, $this->responseCode()); + + $body = $this->authJson('GET', "/api/v1/resource/{$resource->getUuid()}/services", $user); + + self::assertSame(200, $this->responseCode()); + self::assertSame( + $service->getSection()->getUuid(), + $body['data'][0]['service_section']['uuid'], + ); + self::assertSame('بخش لیزر زیربغل', $body['data'][0]['service_section']['name']); + } }