diff --git a/app/Domains/Ticket/Controllers/Scanner/TicketController.php b/app/Domains/Ticket/Controllers/Scanner/TicketController.php index 8895c4b..891cbeb 100644 --- a/app/Domains/Ticket/Controllers/Scanner/TicketController.php +++ b/app/Domains/Ticket/Controllers/Scanner/TicketController.php @@ -3,8 +3,8 @@ namespace App\Domains\Ticket\Controllers\Scanner; use App\Domains\Auth\Models\User; -use App\Domains\Ticket\Requests\ScannerTicketIndexRequest; -use App\Domains\Ticket\Resources\Scanner\ScannedTicketResource; +use App\Domains\Ticket\Requests\ScanAttemptIndexRequest; +use App\Domains\Ticket\Resources\Scanner\ScanAttemptResource; use App\Domains\Ticket\Resources\TicketResource; use App\Domains\Ticket\Services\ScannerTicketService; use App\Http\Controllers\Controller; @@ -15,13 +15,13 @@ class TicketController extends Controller { public function __construct(private readonly ScannerTicketService $ticketService) {} - public function index(ScannerTicketIndexRequest $request): AnonymousResourceCollection + public function index(ScanAttemptIndexRequest $request): AnonymousResourceCollection { /** @var User $scanner */ $scanner = $request->user(); - return ScannedTicketResource::collection( - $this->ticketService->scannedBy($scanner, $request->validated()) + return ScanAttemptResource::collection( + $this->ticketService->attemptsBy($scanner, $request->validated()) ); } diff --git a/app/Domains/Ticket/Requests/ScannerTicketIndexRequest.php b/app/Domains/Ticket/Requests/ScanAttemptIndexRequest.php similarity index 90% rename from app/Domains/Ticket/Requests/ScannerTicketIndexRequest.php rename to app/Domains/Ticket/Requests/ScanAttemptIndexRequest.php index e1e24e8..4bb06af 100644 --- a/app/Domains/Ticket/Requests/ScannerTicketIndexRequest.php +++ b/app/Domains/Ticket/Requests/ScanAttemptIndexRequest.php @@ -4,7 +4,7 @@ namespace App\Domains\Ticket\Requests; use Illuminate\Foundation\Http\FormRequest; -class ScannerTicketIndexRequest extends FormRequest +class ScanAttemptIndexRequest extends FormRequest { public function authorize(): bool { diff --git a/app/Domains/Ticket/Resources/Scanner/ScanAttemptResource.php b/app/Domains/Ticket/Resources/Scanner/ScanAttemptResource.php new file mode 100644 index 0000000..97226de --- /dev/null +++ b/app/Domains/Ticket/Resources/Scanner/ScanAttemptResource.php @@ -0,0 +1,26 @@ + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'data' => $this->data, + 'ticket' => $this->ticket?->ticket, + 'attempted_at' => $this->created_at, + 'result' => $this->result->value, + 'can_view_ticket' => $this->ticket !== null + && $this->result !== ScanAttemptResult::CategoryForbidden, + ]; + } +} diff --git a/app/Domains/Ticket/Resources/Scanner/ScannedTicketResource.php b/app/Domains/Ticket/Resources/Scanner/ScannedTicketResource.php deleted file mode 100644 index ed901fc..0000000 --- a/app/Domains/Ticket/Resources/Scanner/ScannedTicketResource.php +++ /dev/null @@ -1,24 +0,0 @@ - */ - public function toArray(Request $request): array - { - return [ - 'product' => $this->name, - 'id' => $this->id, - 'ticket' => $this->ticket, - 'used_at' => $this->used_at, - 'expires_at' => $this->getEffectiveExpiresAt(), - 'status' => $this->status, - ]; - } -} diff --git a/app/Domains/Ticket/Services/ScannerTicketService.php b/app/Domains/Ticket/Services/ScannerTicketService.php index 9ec7948..c1324c7 100644 --- a/app/Domains/Ticket/Services/ScannerTicketService.php +++ b/app/Domains/Ticket/Services/ScannerTicketService.php @@ -20,31 +20,32 @@ class ScannerTicketService /** * @param array{q?: string|null, page?: int, per_page?: int} $filters - * @return LengthAwarePaginator + * @return LengthAwarePaginator */ - public function scannedBy(User $scanner, array $filters = []): LengthAwarePaginator + public function attemptsBy(User $scanner, array $filters = []): LengthAwarePaginator { $search = trim((string) ($filters['q'] ?? '')); - return $this->baseQuery() + return ScanAttempt::query() + ->with('ticket') ->where('tenant_code', $scanner->tenant_codigo) ->where('scanner_user_id', $scanner->getKey()) ->when($search !== '', function (Builder $query) use ($search): void { - $usedAtDate = $this->parseSearchDate($search); + $attemptedAtDate = $this->parseSearchDate($search); - $query->where(function (Builder $searchQuery) use ($search, $usedAtDate): void { - $searchQuery->where('ticket', 'like', "%{$search}%"); + $query->where(function (Builder $searchQuery) use ($search, $attemptedAtDate): void { + $searchQuery->where('data', 'like', "%{$search}%"); if (ctype_digit($search)) { $searchQuery->orWhere('id', (int) $search); } - if ($usedAtDate !== null) { - $searchQuery->orWhereDate('used_at', $usedAtDate); + if ($attemptedAtDate !== null) { + $searchQuery->orWhereDate('created_at', $attemptedAtDate); } }); }) - ->orderByDesc('used_at') + ->orderByDesc('created_at') ->orderByDesc('id') ->paginateFromRequest() ->withQueryString(); diff --git a/tests/Feature/Ticket/ScannerTicketControllerTest.php b/tests/Feature/Ticket/ScannerTicketControllerTest.php index 01dc65e..ff86ea3 100644 --- a/tests/Feature/Ticket/ScannerTicketControllerTest.php +++ b/tests/Feature/Ticket/ScannerTicketControllerTest.php @@ -62,24 +62,23 @@ class ScannerTicketControllerTest extends TestCase $this->getJson('/api/v1/scanner/tickets')->assertForbidden(); } - public function test_scanner_can_list_only_its_scanned_tickets_using_adminapp_format(): void + public function test_scanner_can_list_only_its_scan_attempts(): void { - $older = $this->createTicket('11111111-1111-4111-8111-111111111111', [ - 'used_at' => now()->subMinutes(2), - 'scanner_user_id' => $this->scanner->id, + $olderTicket = $this->createTicket('11111111-1111-4111-8111-111111111111'); + $newerTicket = $this->createTicket('22222222-2222-4222-8222-222222222222'); + $older = $this->createScanAttempt($olderTicket->ticket, [ + 'ticket_id' => $olderTicket->id, + 'created_at' => now()->subMinutes(2), ]); - $newer = $this->createTicket('22222222-2222-4222-8222-222222222222', [ - 'used_at' => now()->subMinute(), - 'scanner_user_id' => $this->scanner->id, + $newer = $this->createScanAttempt($newerTicket->ticket, [ + 'ticket_id' => $newerTicket->id, + 'created_at' => now()->subMinute(), ]); $otherScanner = User::factory()->create([ 'rol_codigo' => RoleCode::Scanner->value, 'tenant_codigo' => $this->tenant->codigo, ]); - $this->createTicket('33333333-3333-4333-8333-333333333333', [ - 'used_at' => now(), - 'scanner_user_id' => $otherScanner->id, - ]); + $this->createScanAttempt('not-this-scanner', [], $otherScanner); Sanctum::actingAs($this->scanner); @@ -87,45 +86,35 @@ class ScannerTicketControllerTest extends TestCase ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonPath('data.0.id', $newer->id) - ->assertJsonPath('data.0.ticket', $newer->ticket) - ->assertJsonPath('data.0.status', Ticket::STATUS_USED) + ->assertJsonPath('data.0.data', $newer->data) + ->assertJsonPath('data.0.ticket', $newerTicket->ticket) + ->assertJsonPath('data.0.result', ScanAttemptResult::Accepted->value) + ->assertJsonPath('data.0.can_view_ticket', true) ->assertJsonPath('data.1.id', $older->id) ->assertJsonPath('meta.current_page', 1) ->assertJsonPath('meta.total', 2); } - public function test_scanner_ticket_history_supports_id_search_and_pagination(): void + public function test_scanner_attempt_history_supports_data_search_and_pagination(): void { - $matching = $this->createTicket('aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa', [ - 'used_at' => now(), - 'scanner_user_id' => $this->scanner->id, - ]); - $this->createTicket('bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', [ - 'used_at' => now()->subMinute(), - 'scanner_user_id' => $this->scanner->id, - ]); + $matching = $this->createScanAttempt('aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'); + $this->createScanAttempt('bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'); Sanctum::actingAs($this->scanner); $this->getJson('/api/v1/scanner/tickets?q=aaaaaaaa&per_page=1') ->assertOk() ->assertJsonCount(1, 'data') - ->assertJsonPath('data.0.ticket', $matching->ticket) + ->assertJsonPath('data.0.data', $matching->data) ->assertJsonPath('meta.current_page', 1) ->assertJsonPath('meta.per_page', 1) ->assertJsonPath('meta.total', 1); } - public function test_scanner_ticket_history_can_be_searched_by_database_id(): void + public function test_scanner_attempt_history_can_be_searched_by_database_id(): void { - $matching = $this->createTicket('cccccccc-cccc-4ccc-8ccc-cccccccccccc', [ - 'used_at' => now(), - 'scanner_user_id' => $this->scanner->id, - ]); - $this->createTicket('dddddddd-dddd-4ddd-8ddd-dddddddddddd', [ - 'used_at' => now()->subMinute(), - 'scanner_user_id' => $this->scanner->id, - ]); + $matching = $this->createScanAttempt('cccccccc-cccc-4ccc-8ccc-cccccccccccc'); + $this->createScanAttempt('dddddddd-dddd-4ddd-8ddd-dddddddddddd'); Sanctum::actingAs($this->scanner); @@ -135,15 +124,13 @@ class ScannerTicketControllerTest extends TestCase ->assertJsonPath('data.0.id', $matching->id); } - public function test_scanner_ticket_history_can_be_searched_by_used_date(): void + public function test_scanner_attempt_history_can_be_searched_by_attempt_date(): void { - $matching = $this->createTicket('eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee', [ - 'used_at' => '2026-08-11 14:30:00', - 'scanner_user_id' => $this->scanner->id, + $matching = $this->createScanAttempt('eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee', [ + 'created_at' => '2026-08-11 14:30:00', ]); - $this->createTicket('ffffffff-ffff-4fff-8fff-ffffffffffff', [ - 'used_at' => '2026-08-10 14:30:00', - 'scanner_user_id' => $this->scanner->id, + $this->createScanAttempt('ffffffff-ffff-4fff-8fff-ffffffffffff', [ + 'created_at' => '2026-08-10 14:30:00', ]); Sanctum::actingAs($this->scanner); @@ -370,6 +357,25 @@ class ScannerTicketControllerTest extends TestCase $this->assertDatabaseCount('scan_attempts', 0); } + /** @param array $attributes */ + private function createScanAttempt( + ?string $data, + array $attributes = [], + ?User $scanner = null, + ): ScanAttempt { + $scanAttempt = new ScanAttempt; + $scanAttempt->forceFill(array_merge([ + 'tenant_code' => $this->tenant->codigo, + 'scanner_user_id' => ($scanner ?? $this->scanner)->id, + 'data' => $data, + 'result' => ScanAttemptResult::Accepted, + 'resolved_at' => now(), + ], $attributes)); + $scanAttempt->save(); + + return $scanAttempt; + } + /** @param array $attributes */ private function createTicket( string $uuid,