Files
clinicpro/tests/ClinicService/ServiceItemMultiStaffTest.php
hamedandClaude Opus 4.8 9e4ee11831 feat(services): match Figma خدمات page in settings shell + multi-staff
Render the clinic services page (sections → services) inside the settings
sub-navigation shell (SettingsLayout, "خدمات" active) to match the Figma
settings design. Restyle section cards to show the service count and a
status toggle with edit/delete actions, and service cards with labelled
price/duration and personnel chips.

A service can now have multiple personnel: add an additive many-to-many
ServiceItem↔ClinicStaff (staffMembers, EAGER) while keeping the legacy
single `staff` column mirrored for backward compatibility. Endpoints accept
`staff_uuids[]` (falling back to the legacy single `staff_uuid`) and return
`staff_members[]`; the section list now reports `items_count`.

Backfill-safe: pre-migration rows fall back to the single staff in toArray.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 13:28:51 +03:30

119 lines
4.8 KiB
PHP

<?php
namespace App\Tests\ClinicService;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\ClinicService\Repository\ServiceItemRepository;
use App\Doctor\Entity\Doctor;
use App\Staff\Entity\ClinicStaff;
use App\Tests\ApiTestCase;
/**
* A service can carry multiple personnel (staffMembers). toArray must expose
* them as `staff_members`, keep the legacy single `staff` (first member) for
* back-compat, and the section list must report `items_count`.
*/
class ServiceItemMultiStaffTest extends ApiTestCase
{
public function testMultiStaffToArrayAndSectionCount(): void
{
$owner = $this->createUser(['ROLE_DOCTOR']);
$doctor = new Doctor($owner, 'دکتر');
$this->em->persist($doctor);
$this->em->flush();
$s1 = new ClinicStaff('doctor', $doctor->getId(), 'مریم امینی');
$s2 = new ClinicStaff('doctor', $doctor->getId(), 'سحر رحمانی');
$this->em->persist($s1);
$this->em->persist($s2);
$this->em->flush();
$section = new ServiceSection('doctor', $doctor->getId(), 'کندلا');
$item = new ServiceItem($section, 'فول بادی', 3_500_000);
$item->setStaffMembers([$s1, $s2]); // staff already managed (as in the controller)
$this->em->persist($section);
$this->em->persist($item);
$this->em->flush();
$sectionUuid = $section->getUuid();
$itemUuid = $item->getUuid();
$this->em->clear();
/** @var ServiceItemRepository $repo */
$repo = static::getContainer()->get(ServiceItemRepository::class);
$reloaded = $repo->findByUuid($itemUuid); // uuid — db_test is never reset
self::assertNotNull($reloaded);
self::assertCount(2, $reloaded->getStaffMembers());
$arr = $reloaded->toArray();
self::assertCount(2, $arr['staff_members']);
// primary (legacy single) mirrors the first member
self::assertSame('مریم امینی', $arr['staff']['full_name']);
// batch count
$sectionEntity = $reloaded->getSection();
$counts = $repo->countBySections([$sectionEntity]);
self::assertSame(1, $counts[$sectionUuid]);
// endpoint exposes items_count
$resp = $this->authJson('GET', '/api/v1/service-sections', $owner);
self::assertSame(200, $this->responseCode());
$row = $resp['data'][0] ?? [];
self::assertSame(1, $row['items_count'] ?? null);
}
public function testCreateAndUpdateItemWithMultipleStaffViaApi(): void
{
$owner = $this->createUser(['ROLE_DOCTOR']);
$doctor = new Doctor($owner, 'دکتر');
$this->em->persist($doctor);
$this->em->flush();
$a = new ClinicStaff('doctor', $doctor->getId(), 'الف');
$b = new ClinicStaff('doctor', $doctor->getId(), 'ب');
$c = new ClinicStaff('doctor', $doctor->getId(), 'ج');
$section = new ServiceSection('doctor', $doctor->getId(), 'بخش');
foreach ([$a, $b, $c, $section] as $e) { $this->em->persist($e); }
$this->em->flush();
// create with two staff members
$created = $this->authJson('POST', '/api/v1/service-item', $owner, [
'section_uuid' => $section->getUuid(),
'name' => 'سرویس چندنفره',
'price_rials' => 1_000,
'staff_uuids' => [$a->getUuid(), $b->getUuid()],
]);
self::assertSame(201, $this->responseCode());
self::assertCount(2, $created['data']['staff_members']);
// update: replace with a single different staff
$itemUuid = $created['data']['uuid'];
$updated = $this->authJson('PATCH', '/api/v1/service-item/' . $itemUuid, $owner, [
'staff_uuids' => [$c->getUuid()],
]);
self::assertSame(200, $this->responseCode());
self::assertCount(1, $updated['data']['staff_members']);
self::assertSame('ج', $updated['data']['staff_members'][0]['full_name']);
}
public function testLegacySingleStaffFallsBackInToArray(): void
{
$doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر');
$this->em->persist($doctor);
$this->em->flush();
$staff = new ClinicStaff('doctor', $doctor->getId(), 'کاربر قدیمی');
$section = new ServiceSection('doctor', $doctor->getId(), 'بخش');
$item = new ServiceItem($section, 'خدمت قدیمی');
$item->setStaff($staff); // legacy single-staff path, no members
$this->em->persist($staff);
$this->em->persist($section);
$this->em->persist($item);
$this->em->flush();
$arr = $item->toArray();
self::assertCount(1, $arr['staff_members']);
self::assertSame('کاربر قدیمی', $arr['staff_members'][0]['full_name']);
}
}