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,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Unsupervised\Schedular\Offering;
|
||||
|
||||
class Offering {
|
||||
|
||||
public const KIND_PRIVATE_LESSON = 'private_lesson';
|
||||
public const KIND_GROUP_CLASS = 'group_class';
|
||||
|
||||
/**
|
||||
* All valid offering kinds.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public const VALID_KINDS = [ self::KIND_PRIVATE_LESSON, self::KIND_GROUP_CLASS ];
|
||||
|
||||
public const BILLING_ONE_TIME = 'one_time';
|
||||
public const BILLING_FULL_TERM = 'full_term';
|
||||
|
||||
/**
|
||||
* All valid billing modes.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public const VALID_BILLING_MODES = [ self::BILLING_ONE_TIME, self::BILLING_FULL_TERM ];
|
||||
|
||||
public function __construct(
|
||||
public readonly int $instructorId,
|
||||
public readonly string $kind,
|
||||
public readonly string $title,
|
||||
public readonly int $priceCents = 0,
|
||||
public readonly string $currency = 'CAD',
|
||||
public readonly string $billingMode = self::BILLING_ONE_TIME,
|
||||
public readonly ?string $description = null,
|
||||
public readonly ?int $durationMinutes = null,
|
||||
public readonly bool $allowWeekly = false,
|
||||
public readonly ?int $capacity = null,
|
||||
public readonly ?string $termStart = null,
|
||||
public readonly ?string $termEnd = null,
|
||||
public readonly ?string $scheduleNote = null,
|
||||
public readonly bool $isActive = true,
|
||||
public readonly ?int $id = null,
|
||||
) {}
|
||||
|
||||
public static function fromRow( object $row ): self {
|
||||
return new self(
|
||||
instructorId: (int) $row->instructor_id,
|
||||
kind: $row->kind,
|
||||
title: $row->title,
|
||||
priceCents: (int) $row->price_cents,
|
||||
currency: $row->currency,
|
||||
billingMode: $row->billing_mode,
|
||||
description: $row->description,
|
||||
durationMinutes: null !== $row->duration_minutes ? (int) $row->duration_minutes : null,
|
||||
allowWeekly: (bool) $row->allow_weekly,
|
||||
capacity: null !== $row->capacity ? (int) $row->capacity : null,
|
||||
termStart: $row->term_start,
|
||||
termEnd: $row->term_end,
|
||||
scheduleNote: $row->schedule_note,
|
||||
isActive: (bool) $row->is_active,
|
||||
id: (int) $row->id,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plain array representation of the offering.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'instructor_id' => $this->instructorId,
|
||||
'kind' => $this->kind,
|
||||
'title' => $this->title,
|
||||
'description' => $this->description,
|
||||
'duration_minutes' => $this->durationMinutes,
|
||||
'price_cents' => $this->priceCents,
|
||||
'currency' => $this->currency,
|
||||
'billing_mode' => $this->billingMode,
|
||||
'allow_weekly' => $this->allowWeekly,
|
||||
'capacity' => $this->capacity,
|
||||
'term_start' => $this->termStart,
|
||||
'term_end' => $this->termEnd,
|
||||
'schedule_note' => $this->scheduleNote,
|
||||
'is_active' => $this->isActive,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user