- Removed package consumption flags and related properties from PriceQuote. - Eliminated unused domain event publishing for policies and waitlist in Schedule. - Cleaned up BookingEngineSeeder by removing package and policy related logic. - Updated SeedScenariosCommand to reflect removal of policies from output. - Dropped policy, package, treatment course, cancellation, waitlist, and domain event tables in migration. - Removed domain event assertions from tests related to resource blocking.
166 lines
5.6 KiB
PHP
166 lines
5.6 KiB
PHP
<?php
|
||
|
||
namespace App\Tests\Appointment;
|
||
|
||
use App\Appointment\Availability\Entity\ResourceOccupancy;
|
||
use App\Auth\Entity\User;
|
||
use App\Clinic\Entity\Clinic;
|
||
use App\Doctor\Entity\DoctorAddress;
|
||
use App\Resource\Entity\ClinicResource;
|
||
use App\Resource\Entity\ResourceType;
|
||
use App\Tests\ApiTestCase;
|
||
use Doctrine\ORM\EntityManagerInterface;
|
||
|
||
/**
|
||
* مسدودسازی موردی منبع — «این بعدازظهر دستگاه سرویس دارد».
|
||
*
|
||
* با استثنای تقویم فرق دارد: آن الگوی کاری را عوض میکند، این یک بازهٔ مشخص را
|
||
* میبندد. مرز بینشان همان چیزی است که این تستها نگه میدارند.
|
||
*/
|
||
class ResourceBlockTest extends ApiTestCase
|
||
{
|
||
/** @return array{0: User, 1: ClinicResource} */
|
||
private function clinicWithResource(): array
|
||
{
|
||
$user = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']);
|
||
$clinic = new Clinic($user);
|
||
$clinic->setName('کلینیک مسدودسازی');
|
||
$this->em->persist($clinic);
|
||
$this->em->flush();
|
||
|
||
$address = DoctorAddress::forClinic($clinic->getId());
|
||
$address->setName('شعبه');
|
||
$this->em->persist($address);
|
||
|
||
$type = new ResourceType('clinic', (int) $clinic->getId(), 'device', 'دستگاه');
|
||
$this->em->persist($type);
|
||
$this->em->flush();
|
||
|
||
$resource = new ClinicResource($address, $type, 'لیزر ۱');
|
||
$this->em->persist($resource);
|
||
$this->em->flush();
|
||
|
||
return [$user, $resource];
|
||
}
|
||
|
||
public function testBlockingAFreeRangeSucceedsAndIsListed(): void
|
||
{
|
||
[$user, $resource] = $this->clinicWithResource();
|
||
|
||
$start = time() + 86400;
|
||
|
||
$body = $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [
|
||
'starts_at' => $start,
|
||
'ends_at' => $start + 4 * 3600,
|
||
'reason' => 'سرویس دورهای دستگاه',
|
||
]);
|
||
|
||
self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE));
|
||
self::assertSame('سرویس دورهای دستگاه', $body['data']['segment_name']);
|
||
|
||
$list = $this->authJson(
|
||
'GET',
|
||
sprintf('/api/v1/resource/%s/blocks?from=%d&to=%d', $resource->getUuid(), $start - 3600, $start + 86400),
|
||
$user,
|
||
);
|
||
|
||
self::assertCount(1, $list['data']);
|
||
}
|
||
|
||
public function testAnInvertedRangeIsRejected(): void
|
||
{
|
||
[$user, $resource] = $this->clinicWithResource();
|
||
|
||
$start = time() + 86400;
|
||
|
||
$this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [
|
||
'starts_at' => $start,
|
||
'ends_at' => $start - 3600,
|
||
]);
|
||
|
||
self::assertSame(422, $this->responseCode());
|
||
}
|
||
|
||
/** ⭐ مسدودسازی روی بازهای که نوبت دارد، ظرفیت را پس نمیگیرد. */
|
||
public function testBlockingOverABookedRangeIsRejected(): void
|
||
{
|
||
[$user, $resource] = $this->clinicWithResource();
|
||
|
||
$start = time() + 2 * 86400;
|
||
|
||
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||
$reloaded = $em->getRepository(ClinicResource::class)->find($resource->getId());
|
||
|
||
$booked = new ResourceOccupancy($reloaded, $start, $start + 3600);
|
||
$booked->setAppointmentId(4242);
|
||
$em->persist($booked);
|
||
$em->flush();
|
||
|
||
$this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [
|
||
'starts_at' => $start,
|
||
'ends_at' => $start + 7200,
|
||
]);
|
||
|
||
self::assertSame(409, $this->responseCode());
|
||
}
|
||
|
||
/** ⭐ اشغالِ یک نوبت واقعی از این مسیر حذف نمیشود. */
|
||
public function testAnAppointmentOccupancyCannotBeDeletedAsABlock(): void
|
||
{
|
||
[$user, $resource] = $this->clinicWithResource();
|
||
|
||
$start = time() + 3 * 86400;
|
||
|
||
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||
$reloaded = $em->getRepository(ClinicResource::class)->find($resource->getId());
|
||
|
||
$booked = new ResourceOccupancy($reloaded, $start, $start + 3600);
|
||
$booked->setAppointmentId(777);
|
||
$em->persist($booked);
|
||
$em->flush();
|
||
|
||
$this->authJson('DELETE', "/api/v1/resource-block/{$booked->getUuid()}", $user);
|
||
|
||
self::assertSame(422, $this->responseCode());
|
||
}
|
||
|
||
public function testAManualBlockCanBeDeleted(): void
|
||
{
|
||
[$user, $resource] = $this->clinicWithResource();
|
||
|
||
$start = time() + 86400;
|
||
|
||
$created = $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [
|
||
'starts_at' => $start,
|
||
'ends_at' => $start + 3600,
|
||
]);
|
||
|
||
$this->authJson('DELETE', "/api/v1/resource-block/{$created['data']['uuid']}", $user);
|
||
self::assertSame(200, $this->responseCode());
|
||
|
||
$list = $this->authJson(
|
||
'GET',
|
||
sprintf('/api/v1/resource/%s/blocks?from=%d&to=%d', $resource->getUuid(), $start - 3600, $start + 86400),
|
||
$user,
|
||
);
|
||
|
||
self::assertCount(0, $list['data']);
|
||
}
|
||
|
||
public function testAnotherClinicCannotBlockTheResource(): void
|
||
{
|
||
[, $resource] = $this->clinicWithResource();
|
||
[$other] = $this->clinicWithResource();
|
||
|
||
$start = time() + 86400;
|
||
|
||
$this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $other, [
|
||
'starts_at' => $start,
|
||
'ends_at' => $start + 3600,
|
||
]);
|
||
|
||
self::assertSame(404, $this->responseCode());
|
||
}
|
||
|
||
}
|