feat(patient): implement record number pattern management
- 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.
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
<?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']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user