feat(ticket): implement terminal status management and validation
This commit is contained in:
@@ -19,6 +19,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
@@ -121,6 +122,13 @@ class Ticket extends Model
|
||||
return self::statusLabels()[$status] ?? $status;
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (self $ticket): void {
|
||||
$ticket->ensureTerminalStatusTransitionIsAllowed();
|
||||
});
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Tenant, $this> */
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
@@ -219,6 +227,21 @@ class Ticket extends Model
|
||||
return self::statusLabel($this->status);
|
||||
}
|
||||
|
||||
public function markAsDisabled(): void
|
||||
{
|
||||
$this->markAsTerminalStatus(self::STATUS_DISABLED);
|
||||
}
|
||||
|
||||
public function markAsCancelled(): void
|
||||
{
|
||||
$this->markAsTerminalStatus(self::STATUS_CANCELLED);
|
||||
}
|
||||
|
||||
public function markAsRefunded(): void
|
||||
{
|
||||
$this->markAsTerminalStatus(self::STATUS_REFUNDED);
|
||||
}
|
||||
|
||||
protected function valueChangeTenantCode(): string
|
||||
{
|
||||
return $this->tenant_code;
|
||||
@@ -226,9 +249,73 @@ class Ticket extends Model
|
||||
|
||||
private function hasTerminalStatus(): bool
|
||||
{
|
||||
return $this->disabled_at !== null
|
||||
|| $this->cancelled_at !== null
|
||||
|| $this->refunded_at !== null;
|
||||
return $this->terminalStatus() !== null;
|
||||
}
|
||||
|
||||
private function markAsTerminalStatus(string $status): void
|
||||
{
|
||||
$currentStatus = $this->terminalStatus();
|
||||
|
||||
if ($currentStatus === $status) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($currentStatus !== null) {
|
||||
$this->throwTerminalStatusTransitionException();
|
||||
}
|
||||
|
||||
$this->ensureTerminalStatusTransitionIsAllowed($status);
|
||||
|
||||
$this->{self::terminalStatusTimestampColumn($status)} = now();
|
||||
}
|
||||
|
||||
private function ensureTerminalStatusTransitionIsAllowed(?string $targetStatus = null): void
|
||||
{
|
||||
$currentStatus = $this->terminalStatusFromAttributes($this->getRawOriginal());
|
||||
$nextStatus = $targetStatus ?? $this->terminalStatus();
|
||||
|
||||
if ($currentStatus === null || $nextStatus === null || $currentStatus === $nextStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->throwTerminalStatusTransitionException();
|
||||
}
|
||||
|
||||
private function throwTerminalStatusTransitionException(): never
|
||||
{
|
||||
throw ValidationException::withMessages([
|
||||
'status' => 'No se puede cambiar un ticket con estado terminal a otro estado terminal.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function terminalStatus(): ?string
|
||||
{
|
||||
return $this->terminalStatusFromAttributes($this->getAttributes());
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $attributes */
|
||||
private function terminalStatusFromAttributes(array $attributes): ?string
|
||||
{
|
||||
foreach ([
|
||||
self::STATUS_REFUNDED,
|
||||
self::STATUS_CANCELLED,
|
||||
self::STATUS_DISABLED,
|
||||
] as $status) {
|
||||
if (($attributes[self::terminalStatusTimestampColumn($status)] ?? null) !== null) {
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function terminalStatusTimestampColumn(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
self::STATUS_DISABLED => 'disabled_at',
|
||||
self::STATUS_CANCELLED => 'cancelled_at',
|
||||
self::STATUS_REFUNDED => 'refunded_at',
|
||||
};
|
||||
}
|
||||
|
||||
public function getNameAttribute(): string
|
||||
|
||||
Reference in New Issue
Block a user