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>
119 lines
4.8 KiB
PHP
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']);
|
|
}
|
|
}
|