Add Policies domain (drafting, versioning, tracked acceptance)
CI / Coding Standards (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Tests (PHP 8.2) (pull_request) Successful in 56s
CI / Tests (PHP 8.3) (pull_request) Successful in 57s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped

Implements #6: studio admins draft, version, and publish policies; the
public registration gate reads the current published version of each, and
acceptance is recorded against the exact version so a new version must be
re-accepted at the next booking.

- src/Policy/: Policy, PolicyVersion, PolicyAcceptance value objects;
  PolicyRepository, PolicyVersionRepository, AcceptanceRepository;
  PolicyService (orchestrates create/add-draft/publish across the policies
  and versions tables); PolicyEndpoint (REST); PolicyController +
  templates/admin/policies.php (Policies admin menu, manage_policies)
- us_policies, us_policy_versions, us_policy_acceptances tables in Schema
- REST: public GET /policies (current published versions); manage_policies
  for create, add version, edit draft, and publish
- Wiring in Plugin, RestRegistrar, AdminMenu

AcceptanceRepository is built now and consumed by the booking/enrolment
gate in #3/#4.

Also bump PHPStan to --memory-limit=1G in the composer lint script; the
default 128M now crashes the analysis as the codebase has grown.

Tests: tests/Unit/Policy/ (value objects, repositories, service).
composer test (90 total), cs, and PHPStan level 6 all pass.

Refs #6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 15:00:54 -03:00
parent 74fb27ea05
commit 6225e772f8
21 changed files with 1344 additions and 9 deletions
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Unsupervised\Schedular\Tests\Unit\Policy;
use Unsupervised\Schedular\Policy\Policy;
use Unsupervised\Schedular\Policy\PolicyAcceptance;
use Unsupervised\Schedular\Policy\PolicyVersion;
use Unsupervised\Schedular\Tests\Unit\TestCase;
class PolicyValueObjectsTest extends TestCase
{
public function testPolicyFromRowAndToArray(): void
{
$policy = Policy::fromRow((object) [
'id' => '4',
'title' => 'Cancellation',
'slug' => 'cancellation',
'current_version_id' => '9',
]);
self::assertSame(4, $policy->id);
self::assertSame('cancellation', $policy->slug);
self::assertSame(9, $policy->currentVersionId);
self::assertArrayHasKey('current_version_id', $policy->toArray());
}
public function testPolicyHandlesNullCurrentVersion(): void
{
$policy = Policy::fromRow((object) [
'id' => '4',
'title' => 'Cancellation',
'slug' => 'cancellation',
'current_version_id' => null,
]);
self::assertNull($policy->currentVersionId);
}
public function testPolicyVersionFromRowAndStatusHelper(): void
{
$version = PolicyVersion::fromRow((object) [
'id' => '9',
'policy_id' => '4',
'version_number' => '2',
'body' => '<p>Policy</p>',
'status' => PolicyVersion::STATUS_PUBLISHED,
'published_at' => '2026-06-01 10:00:00',
]);
self::assertSame(9, $version->id);
self::assertSame(2, $version->versionNumber);
self::assertTrue($version->isPublished());
self::assertSame('2026-06-01 10:00:00', $version->publishedAt);
}
public function testPolicyVersionDefaultsToDraft(): void
{
$version = new PolicyVersion(4, 1);
self::assertSame(PolicyVersion::STATUS_DRAFT, $version->status);
self::assertFalse($version->isPublished());
self::assertNull($version->publishedAt);
self::assertContains(PolicyVersion::STATUS_ARCHIVED, PolicyVersion::VALID_STATUSES);
}
public function testPolicyAcceptanceFromRowAndToArray(): void
{
$acceptance = PolicyAcceptance::fromRow((object) [
'id' => '1',
'policy_version_id' => '9',
'student_id' => '5',
'registration_type' => PolicyAcceptance::REG_LESSON,
'registration_id' => '12',
'accepted_at' => '2026-06-02 09:00:00',
'ip_address' => '203.0.113.7',
]);
self::assertSame(9, $acceptance->policyVersionId);
self::assertSame(PolicyAcceptance::REG_LESSON, $acceptance->registrationType);
self::assertSame('203.0.113.7', $acceptance->ipAddress);
self::assertArrayHasKey('policy_version_id', $acceptance->toArray());
self::assertContains(PolicyAcceptance::REG_ENROLLMENT, PolicyAcceptance::VALID_REGISTRATION_TYPES);
}
}