Add Offerings domain and studio-admin capabilities
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / Tests (PHP 8.3) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / Tests (PHP 8.3) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 2s
Implements the offerings catalog (#1): private-lesson types and group classes carrying pricing, billing mode (one_time/full_term), duration, capacity, and term details. Adds the src/Offering/ domain (value object, repository, REST endpoint, admin controller + template), the us_offerings table, and an Offerings admin page. Also lands the capability slice of #9: registers the us_studio_admin role and the new capability strings (manage_instructors, manage_offerings, manage_questions, manage_policies, manage_billing, view_all_payments, view_own_payments, export_payments) so offering management gates correctly. Tests: tests/Unit/Offering/ (value object + repository) and a studio-admin case in RoleManagerTest. composer test, cs, and PHPStan level 6 all pass. Refs #1 #9 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Unsupervised\Schedular\Tests\Unit\Offering;
|
||||
|
||||
use Unsupervised\Schedular\Offering\Offering;
|
||||
use Unsupervised\Schedular\Tests\Unit\TestCase;
|
||||
|
||||
class OfferingTest extends TestCase
|
||||
{
|
||||
public function testConstructorAndProperties(): void
|
||||
{
|
||||
$offering = new Offering(
|
||||
instructorId: 5,
|
||||
kind: Offering::KIND_PRIVATE_LESSON,
|
||||
title: '30-min Piano',
|
||||
priceCents: 3500,
|
||||
billingMode: Offering::BILLING_ONE_TIME,
|
||||
durationMinutes: 30,
|
||||
id: 42,
|
||||
);
|
||||
|
||||
self::assertSame(5, $offering->instructorId);
|
||||
self::assertSame(Offering::KIND_PRIVATE_LESSON, $offering->kind);
|
||||
self::assertSame('30-min Piano', $offering->title);
|
||||
self::assertSame(3500, $offering->priceCents);
|
||||
self::assertSame(30, $offering->durationMinutes);
|
||||
self::assertSame(42, $offering->id);
|
||||
}
|
||||
|
||||
public function testDefaults(): void
|
||||
{
|
||||
$offering = new Offering(1, Offering::KIND_GROUP_CLASS, 'Choir');
|
||||
|
||||
self::assertSame(0, $offering->priceCents);
|
||||
self::assertSame('CAD', $offering->currency);
|
||||
self::assertSame(Offering::BILLING_ONE_TIME, $offering->billingMode);
|
||||
self::assertNull($offering->durationMinutes);
|
||||
self::assertFalse($offering->allowWeekly);
|
||||
self::assertNull($offering->capacity);
|
||||
self::assertTrue($offering->isActive);
|
||||
self::assertNull($offering->id);
|
||||
}
|
||||
|
||||
public function testFromRowMapsCorrectlyAndCastsNullables(): void
|
||||
{
|
||||
$row = (object) [
|
||||
'id' => '7',
|
||||
'instructor_id' => '3',
|
||||
'kind' => Offering::KIND_GROUP_CLASS,
|
||||
'title' => 'Year Choir',
|
||||
'description' => 'Weekly choir',
|
||||
'duration_minutes' => null,
|
||||
'price_cents' => '12000',
|
||||
'currency' => 'CAD',
|
||||
'billing_mode' => Offering::BILLING_FULL_TERM,
|
||||
'allow_weekly' => '0',
|
||||
'capacity' => '20',
|
||||
'term_start' => '2026-09-01',
|
||||
'term_end' => '2027-06-30',
|
||||
'schedule_note' => 'Tuesdays 4:00pm',
|
||||
'is_active' => '1',
|
||||
];
|
||||
|
||||
$offering = Offering::fromRow($row);
|
||||
|
||||
self::assertSame(7, $offering->id);
|
||||
self::assertSame(3, $offering->instructorId);
|
||||
self::assertNull($offering->durationMinutes);
|
||||
self::assertSame(12000, $offering->priceCents);
|
||||
self::assertSame(20, $offering->capacity);
|
||||
self::assertSame(Offering::BILLING_FULL_TERM, $offering->billingMode);
|
||||
self::assertTrue($offering->isActive);
|
||||
}
|
||||
|
||||
public function testToArrayContainsExpectedKeys(): void
|
||||
{
|
||||
$offering = new Offering(1, Offering::KIND_PRIVATE_LESSON, 'Lesson', id: 10);
|
||||
$arr = $offering->toArray();
|
||||
|
||||
foreach (['id', 'instructor_id', 'kind', 'title', 'price_cents', 'billing_mode', 'is_active'] as $key) {
|
||||
self::assertArrayHasKey($key, $arr);
|
||||
}
|
||||
}
|
||||
|
||||
public function testValidKindAndBillingConstants(): void
|
||||
{
|
||||
self::assertContains(Offering::KIND_PRIVATE_LESSON, Offering::VALID_KINDS);
|
||||
self::assertContains(Offering::KIND_GROUP_CLASS, Offering::VALID_KINDS);
|
||||
self::assertContains(Offering::BILLING_ONE_TIME, Offering::VALID_BILLING_MODES);
|
||||
self::assertContains(Offering::BILLING_FULL_TERM, Offering::VALID_BILLING_MODES);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user