feat(ticket): add refundable terminal states

This commit is contained in:
2026-09-10 16:10:08 -03:00
parent 1564985259
commit 18f739b712
5 changed files with 208 additions and 3 deletions

View File

@@ -6,6 +6,7 @@ use App\Domains\Logging\Enums\ValueChangeActorType;
use App\Domains\Logging\Models\Concerns\LogsValueChanges;
use App\Domains\Logging\Models\ValueChange;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Ticket\Models\Ticket;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
@@ -52,6 +53,16 @@ class LogsValueChangesTest extends TestCase
$table->timestamps();
});
Schema::create('tickets', function (Blueprint $table): void {
$table->id();
$table->string('tenant_code');
$table->uuid('ticket');
$table->dateTime('used_at')->nullable();
$table->dateTime('disabled_at')->nullable();
$table->dateTime('cancelled_at')->nullable();
$table->dateTime('refunded_at')->nullable();
});
$migration = require database_path('migrations/2026_08_03_000200_create_value_changes_table.php');
$migration->up();
$tenantMigration = require database_path('migrations/2026_08_04_000000_add_tenant_code_to_value_changes_table.php');
@@ -144,6 +155,26 @@ class LogsValueChangesTest extends TestCase
'user_id' => null,
]);
}
public function test_ticket_logs_its_status_changes(): void
{
$ticket = Ticket::query()->create([
'tenant_code' => 'test',
'ticket' => '794606d5-5f69-458d-9de7-03494757d626',
]);
$ticket->update(['disabled_at' => now()]);
$this->assertDatabaseHas('value_changes', [
'tenant_code' => 'test',
'trackable_type' => $ticket->getMorphClass(),
'trackable_id' => $ticket->id,
'attribute' => 'disabled_at',
'old_value' => null,
'actor_type' => ValueChangeActorType::System->value,
'user_id' => null,
]);
}
}
#[Fillable(['name', 'price', 'description'])]

View File

@@ -31,6 +31,9 @@ class TicketTest extends TestCase
'source_catalog_item_id' => '20',
'source_variant_id' => '30',
'used_at' => null,
'disabled_at' => '2026-09-10 10:00:00',
'cancelled_at' => null,
'refunded_at' => null,
'scanner_user_id' => '15',
'user_id' => '10',
]);
@@ -40,6 +43,9 @@ class TicketTest extends TestCase
$this->assertSame(20, $ticket->source_catalog_item_id);
$this->assertSame(30, $ticket->source_variant_id);
$this->assertNull($ticket->used_at);
$this->assertSame('2026-09-10 10:00:00', $ticket->disabled_at->format('Y-m-d H:i:s'));
$this->assertNull($ticket->cancelled_at);
$this->assertNull($ticket->refunded_at);
$this->assertSame(15, $ticket->scanner_user_id);
$this->assertSame(10, $ticket->user_id);
$this->assertInstanceOf(Tenant::class, $ticket->tenant()->getRelated());
@@ -154,6 +160,48 @@ class TicketTest extends TestCase
$this->assertSame(Ticket::STATUS_USED, $ticket->status);
}
public function test_persisted_terminal_statuses_make_the_ticket_invalid(): void
{
foreach ([
'disabled_at' => Ticket::STATUS_DISABLED,
'cancelled_at' => Ticket::STATUS_CANCELLED,
'refunded_at' => Ticket::STATUS_REFUNDED,
] as $timestamp => $status) {
$ticket = new Ticket([$timestamp => now()]);
$this->assertSame($status, $ticket->status);
$this->assertFalse($ticket->is_valid);
$this->assertFalse($ticket->is_expired);
}
}
public function test_it_exposes_every_supported_status(): void
{
$this->assertSame([
Ticket::STATUS_ACTIVE,
Ticket::STATUS_USED,
Ticket::STATUS_EXPIRED,
Ticket::STATUS_DISABLED,
Ticket::STATUS_CANCELLED,
Ticket::STATUS_REFUNDED,
], Ticket::statuses());
$this->assertSame('Inhabilitado', Ticket::statusLabel(Ticket::STATUS_DISABLED));
$this->assertSame('Cancelado', Ticket::statusLabel(Ticket::STATUS_CANCELLED));
$this->assertSame('Reembolsado', Ticket::statusLabel(Ticket::STATUS_REFUNDED));
}
public function test_refunded_has_priority_when_multiple_state_timestamps_exist(): void
{
$ticket = new Ticket([
'disabled_at' => now()->subHours(2),
'cancelled_at' => now()->subHour(),
'refunded_at' => now(),
]);
$this->assertSame(Ticket::STATUS_REFUNDED, $ticket->status);
}
public function test_all_validity_times_in_the_same_group_must_be_active(): void
{
Carbon::setTestNow('2026-08-20 13:00:00');