- Add RecordNumberSettingsController for managing patient record number patterns. - Create RecordNumberPattern entity to represent the pattern configuration. - Implement RecordNumberPatternRepository for database interactions. - Develop RecordNumberGenerator service for generating and validating record numbers. - Add tests for record number generation, backfilling, and API interactions. - Ensure proper access control for viewing and updating patterns based on user roles.
153 lines
6.1 KiB
PHP
153 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Patient;
|
|
|
|
use App\Auth\Entity\User;
|
|
use App\Auth\Entity\UserActiveContext;
|
|
use App\Clinic\Entity\Clinic;
|
|
use App\Doctor\Entity\Doctor;
|
|
use App\Secretary\Entity\DoctorSecretary;
|
|
use App\Tests\ApiTestCase;
|
|
|
|
/**
|
|
* GET/PUT /api/v1/patient-record-number-settings — الگوی شمارهٔ پروندهٔ محیط جاری.
|
|
* خواندن برای هر کسی که بیماران را میبیند؛ نوشتن فقط برای صاحب محیط.
|
|
*/
|
|
class RecordNumberSettingsApiTest extends ApiTestCase
|
|
{
|
|
private const URI = '/api/v1/patient-record-number-settings';
|
|
|
|
/** @return array{0: User, 1: Clinic, 2: Doctor} مالک کلینیک + کلینیک + پزشکِ عضو */
|
|
private function clinic(): array
|
|
{
|
|
$owner = $this->createUser(['ROLE_CLINIC']);
|
|
$clinic = new Clinic($owner);
|
|
$this->em->persist($clinic);
|
|
|
|
$doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر عضو');
|
|
$this->em->persist($doctor);
|
|
$clinic->getDoctors()->add($doctor);
|
|
|
|
$this->em->persist(new UserActiveContext($owner, $clinic->getUuid(), 'clinic'));
|
|
$this->em->flush();
|
|
|
|
return [$owner, $clinic, $doctor];
|
|
}
|
|
|
|
public function testOwnerReadsDefaultsBeforeAnythingIsSaved(): void
|
|
{
|
|
[$owner] = $this->clinic();
|
|
|
|
$res = $this->authJson('GET', self::URI, $owner);
|
|
self::assertSame(200, $this->responseCode());
|
|
self::assertFalse($res['data']['enabled']);
|
|
self::assertSame('none', $res['data']['reset_policy']);
|
|
self::assertSame(0, $res['data']['counter']);
|
|
self::assertTrue($res['data']['can_edit']);
|
|
}
|
|
|
|
/** ✅ موفق: ذخیرهٔ الگو و پیشنمایش شمارهٔ بعدی. */
|
|
public function testOwnerSavesPatternAndGetsPreview(): void
|
|
{
|
|
[$owner] = $this->clinic();
|
|
|
|
$res = $this->authJson('PUT', self::URI, $owner, [
|
|
'enabled' => true,
|
|
'pattern' => 'MD-{SEQ:4}',
|
|
'reset_policy' => 'none',
|
|
]);
|
|
|
|
self::assertSame(200, $this->responseCode());
|
|
self::assertTrue($res['data']['enabled']);
|
|
self::assertSame('MD-{SEQ:4}', $res['data']['pattern']);
|
|
self::assertSame('MD-0001', $res['data']['next_preview']);
|
|
|
|
// GET بعدی همان مقدارِ ذخیرهشده را میدهد (نه پیشفرض).
|
|
$read = $this->authJson('GET', self::URI, $owner);
|
|
self::assertSame('MD-{SEQ:4}', $read['data']['pattern']);
|
|
}
|
|
|
|
/** ❌ خطا: منشی نه مینویسد، نه `can_edit` میگیرد. */
|
|
public function testSecretaryCannotWriteButCanRead(): void
|
|
{
|
|
[, $clinic, $doctor] = $this->clinic();
|
|
|
|
$secretary = $this->createUser(['ROLE_SECRETARY']);
|
|
$this->em->persist(new DoctorSecretary($doctor, $secretary, $clinic));
|
|
$this->em->persist(new UserActiveContext($secretary, $clinic->getUuid(), 'clinic'));
|
|
$this->em->flush();
|
|
|
|
$read = $this->authJson('GET', self::URI, $secretary);
|
|
self::assertSame(200, $this->responseCode());
|
|
self::assertFalse($read['data']['can_edit']);
|
|
|
|
$this->authJson('PUT', self::URI, $secretary, [
|
|
'enabled' => true, 'pattern' => 'X-{SEQ}', 'reset_policy' => 'none',
|
|
]);
|
|
self::assertSame(403, $this->responseCode());
|
|
}
|
|
|
|
/** ❌ خطا: پزشکِ مهمانِ کلینیک هم صاحب آن محیط نیست. */
|
|
public function testGuestDoctorCannotChangeTheClinicPattern(): void
|
|
{
|
|
[, $clinic, $doctor] = $this->clinic();
|
|
$this->em->persist(new UserActiveContext($doctor->getUser(), $clinic->getUuid(), 'clinic'));
|
|
$this->em->flush();
|
|
|
|
$this->authJson('PUT', self::URI, $doctor->getUser(), [
|
|
'enabled' => true, 'pattern' => 'X-{SEQ}', 'reset_policy' => 'none',
|
|
]);
|
|
self::assertSame(403, $this->responseCode());
|
|
}
|
|
|
|
/** ❌ خطا: الگوهای نامعتبر همگی ۴۲۲ با فیلد درست. */
|
|
public function testInvalidPatternsAreRejected(): void
|
|
{
|
|
[$owner] = $this->clinic();
|
|
|
|
foreach ([
|
|
['MD-{FOO}-{SEQ}', 'none'], // توکن ناشناخته
|
|
['MD-0001', 'none'], // بدون {SEQ}
|
|
['', 'none'], // خالی
|
|
['{YY}-{SEQ}', 'none'], // سال بدون ریست
|
|
['{MM}-{SEQ}', 'yearly'], // ماه بدون ریست ماهانه
|
|
] as [$pattern, $reset]) {
|
|
$res = $this->authJson('PUT', self::URI, $owner, [
|
|
'enabled' => true, 'pattern' => $pattern, 'reset_policy' => $reset,
|
|
]);
|
|
self::assertSame(422, $this->responseCode(), "pattern: $pattern");
|
|
self::assertSame('pattern', $res['errors'][0]['field'] ?? null);
|
|
}
|
|
|
|
// سیاست ریستِ ناشناخته هم رد میشود، ولی فیلدش reset_policy است.
|
|
$res = $this->authJson('PUT', self::URI, $owner, [
|
|
'enabled' => true, 'pattern' => 'X-{SEQ}', 'reset_policy' => 'weekly',
|
|
]);
|
|
self::assertSame(422, $this->responseCode());
|
|
self::assertSame('reset_policy', $res['errors'][0]['field'] ?? null);
|
|
}
|
|
|
|
/** ⚠️ مرزی: تغییر الگو شمارنده را صفر نمیکند — شمارههای صادرشده وجود دارند. */
|
|
public function testChangingThePatternKeepsTheCounter(): void
|
|
{
|
|
[$owner, $clinic] = $this->clinic();
|
|
|
|
$this->authJson('PUT', self::URI, $owner, [
|
|
'enabled' => true, 'pattern' => 'A-{SEQ:3}', 'reset_policy' => 'none',
|
|
]);
|
|
|
|
$repo = $this->em->getRepository(\App\Patient\Entity\RecordNumberPattern::class);
|
|
$row = $repo->findForPair('clinic', $clinic->getId());
|
|
$row->incrementCounter();
|
|
$row->incrementCounter();
|
|
$this->em->flush();
|
|
|
|
$res = $this->authJson('PUT', self::URI, $owner, [
|
|
'enabled' => true, 'pattern' => 'B-{SEQ:3}', 'reset_policy' => 'none',
|
|
]);
|
|
|
|
self::assertSame(2, $res['data']['counter']);
|
|
self::assertSame('B-003', $res['data']['next_preview']);
|
|
}
|
|
}
|