feat(admin): expose ticket refund states
This commit is contained in:
@@ -124,11 +124,7 @@ class TicketFilterFormService
|
|||||||
'required' => false,
|
'required' => false,
|
||||||
'default' => null,
|
'default' => null,
|
||||||
'placeholder' => 'Estado',
|
'placeholder' => 'Estado',
|
||||||
'options' => [
|
'options' => Ticket::statusOptions(),
|
||||||
['value' => Ticket::STATUS_ACTIVE, 'label' => 'Activo'],
|
|
||||||
['value' => Ticket::STATUS_USED, 'label' => 'Usado'],
|
|
||||||
['value' => Ticket::STATUS_EXPIRED, 'label' => 'Vencido'],
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,11 +236,7 @@ class TicketFormService
|
|||||||
?: $left['label'] <=> $right['label']);
|
?: $left['label'] <=> $right['label']);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'statuses' => [
|
'statuses' => Ticket::statusOptions(),
|
||||||
['value' => Ticket::STATUS_ACTIVE, 'label' => 'Activo'],
|
|
||||||
['value' => Ticket::STATUS_USED, 'label' => 'Usado'],
|
|
||||||
['value' => Ticket::STATUS_EXPIRED, 'label' => 'Vencido'],
|
|
||||||
],
|
|
||||||
'categories' => array_values(array_map(
|
'categories' => array_values(array_map(
|
||||||
fn (array $category): array => [
|
fn (array $category): array => [
|
||||||
'value' => $category['value'],
|
'value' => $category['value'],
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class PurchaseItemResource extends JsonResource
|
|||||||
'quantity' => (int) $this->cantidad,
|
'quantity' => (int) $this->cantidad,
|
||||||
'unit_price' => $this->formatMoney($this->precio_unitario),
|
'unit_price' => $this->formatMoney($this->precio_unitario),
|
||||||
'line_total' => $this->formatMoney($this->total),
|
'line_total' => $this->formatMoney($this->total),
|
||||||
|
'refunded_amount' => $this->formatMoney($this->refunded_amount),
|
||||||
'source_catalog_item_id' => $this->source_catalog_item_id,
|
'source_catalog_item_id' => $this->source_catalog_item_id,
|
||||||
'source_variant_id' => $this->source_variant_id,
|
'source_variant_id' => $this->source_variant_id,
|
||||||
'item_details' => [
|
'item_details' => [
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class SaleDetailResource extends JsonResource
|
|||||||
'quantity' => (int) $item->cantidad,
|
'quantity' => (int) $item->cantidad,
|
||||||
'unit_price' => $this->formatMoney($item->precio_unitario),
|
'unit_price' => $this->formatMoney($item->precio_unitario),
|
||||||
'total' => $this->formatMoney($item->total),
|
'total' => $this->formatMoney($item->total),
|
||||||
|
'refunded_amount' => $this->formatMoney($item->refunded_amount),
|
||||||
])->values(),
|
])->values(),
|
||||||
'total' => $this->formatMoney($this->total),
|
'total' => $this->formatMoney($this->total),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class SaleTicketResource extends JsonResource
|
|||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'expires_at' => $this->getEffectiveExpiresAt(),
|
'expires_at' => $this->getEffectiveExpiresAt(),
|
||||||
'status' => $this->status,
|
'status' => $this->status,
|
||||||
|
'status_label' => $this->status_label,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,7 @@ class AdminAppTicketIndexRequest extends FormRequest
|
|||||||
'status' => [
|
'status' => [
|
||||||
'sometimes',
|
'sometimes',
|
||||||
'nullable',
|
'nullable',
|
||||||
Rule::in([
|
Rule::in(Ticket::statuses()),
|
||||||
Ticket::STATUS_ACTIVE,
|
|
||||||
Ticket::STATUS_USED,
|
|
||||||
Ticket::STATUS_EXPIRED,
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
'page' => ['sometimes', 'integer', 'min:1'],
|
'page' => ['sometimes', 'integer', 'min:1'],
|
||||||
'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'],
|
'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'],
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class TicketResource extends JsonResource
|
|||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'tenant_code' => $this->tenant_code,
|
'tenant_code' => $this->tenant_code,
|
||||||
'ticket' => $this->ticket,
|
'ticket' => $this->ticket,
|
||||||
|
'status' => $this->status,
|
||||||
|
'status_label' => $this->status_label,
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'client' => $this->user?->nombre_apellido,
|
'client' => $this->user?->nombre_apellido,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class AdminAppTicketRowService
|
|||||||
?? $ticket->sourceCatalogItem?->nombre
|
?? $ticket->sourceCatalogItem?->nombre
|
||||||
?? $ticket->name,
|
?? $ticket->name,
|
||||||
'amount' => $purchaseItem?->precio_unitario,
|
'amount' => $purchaseItem?->precio_unitario,
|
||||||
|
'refunded_amount' => $purchaseItem?->refunded_amount,
|
||||||
'client' => $purchaseItem?->purchase?->nombre_apellido ?? $ticket->user?->nombre_apellido,
|
'client' => $purchaseItem?->purchase?->nombre_apellido ?? $ticket->user?->nombre_apellido,
|
||||||
'status' => $ticket->status,
|
'status' => $ticket->status,
|
||||||
'scanned_by' => $ticket->scannerUser?->nombre_apellido,
|
'scanned_by' => $ticket->scannerUser?->nombre_apellido,
|
||||||
@@ -95,11 +96,7 @@ class AdminAppTicketRowService
|
|||||||
return match ($type) {
|
return match ($type) {
|
||||||
'order_number' => '#'.$value,
|
'order_number' => '#'.$value,
|
||||||
'currency' => '$'.number_format((float) $value, 2, ',', '.'),
|
'currency' => '$'.number_format((float) $value, 2, ',', '.'),
|
||||||
'status' => match ((string) $value) {
|
'status' => Ticket::statusLabel((string) $value),
|
||||||
Ticket::STATUS_USED => 'Usado',
|
|
||||||
Ticket::STATUS_EXPIRED => 'Vencido',
|
|
||||||
default => 'Activo',
|
|
||||||
},
|
|
||||||
default => (string) $value,
|
default => (string) $value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,13 +255,41 @@ class AdminAppTicketService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($status === Ticket::STATUS_USED) {
|
if ($status === Ticket::STATUS_USED) {
|
||||||
$query->whereNotNull('used_at');
|
$query
|
||||||
|
->whereNotNull('used_at')
|
||||||
|
->whereNull('disabled_at')
|
||||||
|
->whereNull('cancelled_at')
|
||||||
|
->whereNull('refunded_at');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestampColumn = match ($status) {
|
||||||
|
Ticket::STATUS_DISABLED => 'disabled_at',
|
||||||
|
Ticket::STATUS_CANCELLED => 'cancelled_at',
|
||||||
|
Ticket::STATUS_REFUNDED => 'refunded_at',
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($timestampColumn !== null) {
|
||||||
|
$query->whereNotNull($timestampColumn);
|
||||||
|
|
||||||
|
if ($status === Ticket::STATUS_DISABLED) {
|
||||||
|
$query->whereNull('cancelled_at')->whereNull('refunded_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($status === Ticket::STATUS_CANCELLED) {
|
||||||
|
$query->whereNull('refunded_at');
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matchingIds = (clone $query)
|
$matchingIds = (clone $query)
|
||||||
->whereNull('used_at')
|
->whereNull('used_at')
|
||||||
|
->whereNull('disabled_at')
|
||||||
|
->whereNull('cancelled_at')
|
||||||
|
->whereNull('refunded_at')
|
||||||
->with(TicketValidityResolver::RELATIONS)
|
->with(TicketValidityResolver::RELATIONS)
|
||||||
->get()
|
->get()
|
||||||
->filter(fn (Ticket $ticket): bool => $ticket->status === $status)
|
->filter(fn (Ticket $ticket): bool => $ticket->status === $status)
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ class AdminAppTicketFilterFormControllerTest extends TestCase
|
|||||||
['value' => 'active', 'label' => 'Activo'],
|
['value' => 'active', 'label' => 'Activo'],
|
||||||
['value' => 'used', 'label' => 'Usado'],
|
['value' => 'used', 'label' => 'Usado'],
|
||||||
['value' => 'expired', 'label' => 'Vencido'],
|
['value' => 'expired', 'label' => 'Vencido'],
|
||||||
|
['value' => 'disabled', 'label' => 'Inhabilitado'],
|
||||||
|
['value' => 'cancelled', 'label' => 'Cancelado'],
|
||||||
|
['value' => 'refunded', 'label' => 'Reembolsado'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ class AdminAppTicketFormControllerTest extends TestCase
|
|||||||
['value' => 'active', 'label' => 'Activo'],
|
['value' => 'active', 'label' => 'Activo'],
|
||||||
['value' => 'used', 'label' => 'Usado'],
|
['value' => 'used', 'label' => 'Usado'],
|
||||||
['value' => 'expired', 'label' => 'Vencido'],
|
['value' => 'expired', 'label' => 'Vencido'],
|
||||||
|
['value' => 'disabled', 'label' => 'Inhabilitado'],
|
||||||
|
['value' => 'cancelled', 'label' => 'Cancelado'],
|
||||||
|
['value' => 'refunded', 'label' => 'Reembolsado'],
|
||||||
],
|
],
|
||||||
'categories' => [
|
'categories' => [
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -507,12 +507,14 @@ class AdminAppSaleControllerTest extends TestCase
|
|||||||
'id' => $firstTicket->id,
|
'id' => $firstTicket->id,
|
||||||
'expires_at' => null,
|
'expires_at' => null,
|
||||||
'status' => Ticket::STATUS_ACTIVE,
|
'status' => Ticket::STATUS_ACTIVE,
|
||||||
|
'status_label' => 'Activo',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'product' => 'Abono general',
|
'product' => 'Abono general',
|
||||||
'id' => $usedTicket->id,
|
'id' => $usedTicket->id,
|
||||||
'expires_at' => null,
|
'expires_at' => null,
|
||||||
'status' => Ticket::STATUS_USED,
|
'status' => Ticket::STATUS_USED,
|
||||||
|
'status_label' => 'Usado',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class AdminAppTicketControllerTest extends TestCase
|
|||||||
->assertJsonPath('data.0.tenant_code', $tenant->codigo)
|
->assertJsonPath('data.0.tenant_code', $tenant->codigo)
|
||||||
->assertJsonPath('data.0.values.id', $ticket->id)
|
->assertJsonPath('data.0.values.id', $ticket->id)
|
||||||
->assertJsonPath('data.0.values.status', Ticket::STATUS_ACTIVE)
|
->assertJsonPath('data.0.values.status', Ticket::STATUS_ACTIVE)
|
||||||
|
->assertJsonPath('data.0.status_label', 'Activo')
|
||||||
->assertJsonMissingPath('data.0.values.ticket')
|
->assertJsonMissingPath('data.0.values.ticket')
|
||||||
->assertJsonPath('data.0.values.date', '-')
|
->assertJsonPath('data.0.values.date', '-')
|
||||||
->assertJsonPath('data.0.values.size', '-')
|
->assertJsonPath('data.0.values.size', '-')
|
||||||
@@ -335,6 +336,7 @@ class AdminAppTicketControllerTest extends TestCase
|
|||||||
->assertJsonPath('data.0.order_number', $purchase->id)
|
->assertJsonPath('data.0.order_number', $purchase->id)
|
||||||
->assertJsonPath('data.0.product', 'Remera')
|
->assertJsonPath('data.0.product', 'Remera')
|
||||||
->assertJsonPath('data.0.amount', '8000.00')
|
->assertJsonPath('data.0.amount', '8000.00')
|
||||||
|
->assertJsonPath('data.0.refunded_amount', '0.00')
|
||||||
->assertJsonPath('data.0.status', Ticket::STATUS_USED)
|
->assertJsonPath('data.0.status', Ticket::STATUS_USED)
|
||||||
->assertJsonPath('data.0.scanned_by', $admin->nombre_apellido)
|
->assertJsonPath('data.0.scanned_by', $admin->nombre_apellido)
|
||||||
->assertJsonPath('data.0.variant_properties.0.code', 'size')
|
->assertJsonPath('data.0.variant_properties.0.code', 'size')
|
||||||
@@ -498,6 +500,28 @@ class AdminAppTicketControllerTest extends TestCase
|
|||||||
->assertJsonPath('data.0.id', $active->id);
|
->assertJsonPath('data.0.id', $active->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_it_filters_persisted_ticket_statuses(): void
|
||||||
|
{
|
||||||
|
$tenant = $this->createTenant('ticket-statuses');
|
||||||
|
$admin = $this->createAdminAppUser($tenant);
|
||||||
|
$this->grantTicketsMenu($tenant);
|
||||||
|
Sanctum::actingAs($admin);
|
||||||
|
|
||||||
|
foreach ([
|
||||||
|
Ticket::STATUS_DISABLED => 'disabled_at',
|
||||||
|
Ticket::STATUS_CANCELLED => 'cancelled_at',
|
||||||
|
Ticket::STATUS_REFUNDED => 'refunded_at',
|
||||||
|
] as $status => $timestamp) {
|
||||||
|
$ticket = $this->createTicket($tenant, $admin, [$timestamp => now()]);
|
||||||
|
|
||||||
|
$this->getJson('/api/v1/adminapp/tenant/tickets?status='.$status)
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonCount(1, 'data')
|
||||||
|
->assertJsonPath('data.0.id', $ticket->id)
|
||||||
|
->assertJsonPath('data.0.status', $status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function test_it_downloads_filtered_ticket_reports(): void
|
public function test_it_downloads_filtered_ticket_reports(): void
|
||||||
{
|
{
|
||||||
$tenant = $this->createTenant('fiesta_futbol_infantil');
|
$tenant = $this->createTenant('fiesta_futbol_infantil');
|
||||||
|
|||||||
@@ -272,6 +272,11 @@ class ScannerTicketControllerTest extends TestCase
|
|||||||
->assertJsonPath('data.ticket.scanner_user_id', $this->scanner->id)
|
->assertJsonPath('data.ticket.scanner_user_id', $this->scanner->id)
|
||||||
->assertJsonPath('data.ticket.is_valid', false)
|
->assertJsonPath('data.ticket.is_valid', false)
|
||||||
->assertJsonPath('data.ticket.is_used', true)
|
->assertJsonPath('data.ticket.is_used', true)
|
||||||
|
->assertJsonPath('data.ticket.status', Ticket::STATUS_USED)
|
||||||
|
->assertJsonPath('data.ticket.status_label', 'Usado')
|
||||||
|
->assertJsonMissingPath('data.ticket.disabled_at')
|
||||||
|
->assertJsonMissingPath('data.ticket.cancelled_at')
|
||||||
|
->assertJsonMissingPath('data.ticket.refunded_at')
|
||||||
->assertJsonPath('data.ticket.client', $this->ticketOwner->nombre_apellido)
|
->assertJsonPath('data.ticket.client', $this->ticketOwner->nombre_apellido)
|
||||||
->assertJsonPath('data.client.id', $this->ticketOwner->id)
|
->assertJsonPath('data.client.id', $this->ticketOwner->id)
|
||||||
->assertJsonPath('data.client.nombre_apellido', $this->ticketOwner->nombre_apellido);
|
->assertJsonPath('data.client.nombre_apellido', $this->ticketOwner->nombre_apellido);
|
||||||
@@ -280,6 +285,14 @@ class ScannerTicketControllerTest extends TestCase
|
|||||||
'id' => $ticket->id,
|
'id' => $ticket->id,
|
||||||
'scanner_user_id' => $this->scanner->id,
|
'scanner_user_id' => $this->scanner->id,
|
||||||
]);
|
]);
|
||||||
|
$this->assertDatabaseHas('value_changes', [
|
||||||
|
'tenant_code' => $this->tenant->codigo,
|
||||||
|
'trackable_type' => $ticket->getMorphClass(),
|
||||||
|
'trackable_id' => $ticket->id,
|
||||||
|
'attribute' => 'used_at',
|
||||||
|
'old_value' => null,
|
||||||
|
'user_id' => $this->scanner->id,
|
||||||
|
]);
|
||||||
$this->assertNotNull($ticket->fresh()->used_at);
|
$this->assertNotNull($ticket->fresh()->used_at);
|
||||||
|
|
||||||
$scanAttempt = ScanAttempt::query()->sole();
|
$scanAttempt = ScanAttempt::query()->sole();
|
||||||
|
|||||||
Reference in New Issue
Block a user