feat(event): support ticket refund configuration in event endpoints

This commit is contained in:
2026-09-11 16:22:46 -03:00
parent c0c19c9c01
commit 4bb4f526e4
4 changed files with 117 additions and 0 deletions

View File

@@ -33,6 +33,16 @@ class UpdateEventRequest extends FormRequest
'contact.whatsapp_url' => ['nullable', 'url', 'max:2048'],
'contact.instagram_url' => ['nullable', 'url', 'max:2048'],
'contact.facebook_url' => ['nullable', 'url', 'max:2048'],
'allow_ticket_refund' => ['sometimes', 'boolean'],
'allow_ticket_total_refund' => ['sometimes', 'boolean'],
'allow_ticket_partial_refund' => ['sometimes', 'boolean'],
'ticket_partial_refund_percentage' => [
'sometimes',
'numeric',
'decimal:0,2',
'min:0',
'max:99.99',
],
];
}
@@ -49,6 +59,41 @@ class UpdateEventRequest extends FormRequest
'The social media field is required.'
);
}
if (! array_key_exists('allow_ticket_refund', $input)) {
return;
}
foreach ([
'allow_ticket_total_refund',
'allow_ticket_partial_refund',
'ticket_partial_refund_percentage',
] as $field) {
if (! array_key_exists($field, $input)) {
$validator->errors()->add($field, 'El campo es obligatorio.');
}
}
$totalEnabled = $this->boolean('allow_ticket_total_refund');
$partialEnabled = $this->boolean('allow_ticket_partial_refund');
$refundEnabled = $this->boolean('allow_ticket_refund');
if ($refundEnabled && ! $totalEnabled && ! $partialEnabled) {
$validator->errors()->add(
'allow_ticket_refund',
'Seleccioná al menos un tipo de reembolso.'
);
}
if ($refundEnabled
&& $partialEnabled
&& (float) ($input['ticket_partial_refund_percentage'] ?? 0) <= 0) {
$validator->errors()->add(
'ticket_partial_refund_percentage',
'Ingresá un porcentaje mayor que cero para el reembolso parcial.'
);
}
},
];
}

View File

@@ -18,6 +18,10 @@ class EventResource extends JsonResource
'id' => $this->id,
'title' => $this->event_title,
'location' => $this->event_location,
'allow_ticket_refund' => $this->allow_ticket_refund,
'allow_ticket_total_refund' => $this->allow_ticket_total_refund,
'allow_ticket_partial_refund' => $this->allow_ticket_partial_refund,
'ticket_partial_refund_percentage' => $this->ticket_partial_refund_percentage,
'dates' => EventDateResource::collection($this->eventDates),
'social_media' => $this->socialMedia->map(fn ($item): array => [
'code' => $item->code,

View File

@@ -34,6 +34,12 @@ class EventService
$tenant->update([
'event_title' => $data['title'],
'event_location' => $data['location'],
...array_intersect_key($data, array_flip([
'allow_ticket_refund',
'allow_ticket_total_refund',
'allow_ticket_partial_refund',
'ticket_partial_refund_percentage',
])),
]);
if (array_key_exists('social_media', $data)) {

View File

@@ -51,6 +51,10 @@ class AdminAppEventControllerTest extends TestCase
->assertOk()
->assertJsonPath('data.title', 'Festival Acme')
->assertJsonPath('data.location', 'Predio Ferial, Rosario')
->assertJsonPath('data.allow_ticket_refund', true)
->assertJsonPath('data.allow_ticket_total_refund', true)
->assertJsonPath('data.allow_ticket_partial_refund', true)
->assertJsonPath('data.ticket_partial_refund_percentage', '25.50')
->assertJsonCount(0, 'data.dates')
->assertJsonPath('data.contact.whatsapp_url', 'https://wa.me/5493415550101')
->assertJsonPath('data.contact.instagram_url', 'https://instagram.com/acme')
@@ -62,6 +66,10 @@ class AdminAppEventControllerTest extends TestCase
'event_title' => 'Festival Acme',
'event_location' => 'Predio Ferial, Rosario',
'event_date_text' => null,
'allow_ticket_refund' => true,
'allow_ticket_total_refund' => true,
'allow_ticket_partial_refund' => true,
'ticket_partial_refund_percentage' => 25.50,
]);
$this->assertDatabaseCount('event_dates', 0);
$this->assertDatabaseHas('tenant_social_media', [
@@ -71,6 +79,56 @@ class AdminAppEventControllerTest extends TestCase
]);
}
public function test_disabling_refunds_preserves_the_configured_types_and_percentage(): void
{
$tenant = $this->createTenant('acme');
$tenant->update([
'allow_ticket_refund' => true,
'allow_ticket_total_refund' => true,
'allow_ticket_partial_refund' => true,
'ticket_partial_refund_percentage' => 35.50,
]);
Sanctum::actingAs($this->createAdminAppUser($tenant));
$payload = $this->eventPayload();
$payload['allow_ticket_refund'] = false;
$payload['ticket_partial_refund_percentage'] = 35.50;
$this->putJson('/api/v1/adminapp/tenant/event', $payload)
->assertOk()
->assertJsonPath('data.allow_ticket_refund', false)
->assertJsonPath('data.allow_ticket_total_refund', true)
->assertJsonPath('data.allow_ticket_partial_refund', true)
->assertJsonPath('data.ticket_partial_refund_percentage', '35.50');
$tenant->refresh();
$this->assertFalse($tenant->allow_refund());
$this->assertTrue($tenant->allow_ticket_total_refund);
$this->assertTrue($tenant->allow_ticket_partial_refund);
$this->assertSame('35.50', $tenant->ticket_partial_refund_percentage);
}
public function test_enabled_refunds_require_a_type_and_a_valid_partial_percentage(): void
{
$tenant = $this->createTenant('acme');
Sanctum::actingAs($this->createAdminAppUser($tenant));
$payload = $this->eventPayload();
$payload['allow_ticket_total_refund'] = false;
$payload['allow_ticket_partial_refund'] = false;
$this->putJson('/api/v1/adminapp/tenant/event', $payload)
->assertUnprocessable()
->assertJsonValidationErrors('allow_ticket_refund');
$payload['allow_ticket_partial_refund'] = true;
$payload['ticket_partial_refund_percentage'] = 0;
$this->putJson('/api/v1/adminapp/tenant/event', $payload)
->assertUnprocessable()
->assertJsonValidationErrors('ticket_partial_refund_percentage');
}
public function test_an_adminapp_user_can_read_only_its_tenant_active_event(): void
{
$tenant = $this->createTenant('acme');
@@ -375,6 +433,10 @@ class AdminAppEventControllerTest extends TestCase
return [
'title' => 'Festival Acme',
'location' => 'Predio Ferial, Rosario',
'allow_ticket_refund' => true,
'allow_ticket_total_refund' => true,
'allow_ticket_partial_refund' => true,
'ticket_partial_refund_percentage' => 25.50,
'contact' => [
'whatsapp_url' => 'https://wa.me/5493415550101',
'instagram_url' => 'https://instagram.com/acme',