diff --git a/app/Domains/Tenant/Models/Tenant.php b/app/Domains/Tenant/Models/Tenant.php index 7c6938f..e575def 100644 --- a/app/Domains/Tenant/Models/Tenant.php +++ b/app/Domains/Tenant/Models/Tenant.php @@ -52,6 +52,7 @@ use Illuminate\Support\Facades\Schema; 'checkout_editing_policy', 'display_cart_item_images', 'scanner_category_validation_enabled', + 'allow_ticket_refund', 'allow_ticket_total_refund', 'allow_ticket_partial_refund', 'ticket_partial_refund_percentage', @@ -75,6 +76,7 @@ class Tenant extends Model 'checkout_editing_policy' => CartEditingPolicy::Disabled->value, 'display_cart_item_images' => true, 'scanner_category_validation_enabled' => true, + 'allow_ticket_refund' => false, 'allow_ticket_total_refund' => false, 'allow_ticket_partial_refund' => false, 'ticket_partial_refund_percentage' => 0, @@ -114,12 +116,14 @@ class Tenant extends Model public function allow_refund(): bool { - return (bool) $this->allow_ticket_total_refund || $this->allow_partial_refund(); + return (bool) $this->allow_ticket_refund + && ((bool) $this->allow_ticket_total_refund || $this->allow_partial_refund()); } public function allow_partial_refund(): bool { - return (bool) $this->allow_ticket_partial_refund + return (bool) $this->allow_ticket_refund + && (bool) $this->allow_ticket_partial_refund && $this->ticket_partial_refund_percentage !== null && (float) $this->ticket_partial_refund_percentage > 0; } @@ -157,6 +161,7 @@ class Tenant extends Model 'checkout_editing_policy' => CartEditingPolicy::class, 'display_cart_item_images' => 'boolean', 'scanner_category_validation_enabled' => 'boolean', + 'allow_ticket_refund' => 'boolean', 'allow_ticket_total_refund' => 'boolean', 'allow_ticket_partial_refund' => 'boolean', 'ticket_partial_refund_percentage' => 'decimal:2', diff --git a/app/Domains/Tenant/Requests/StoreTenantRequest.php b/app/Domains/Tenant/Requests/StoreTenantRequest.php index c7bfef5..2dc435b 100644 --- a/app/Domains/Tenant/Requests/StoreTenantRequest.php +++ b/app/Domains/Tenant/Requests/StoreTenantRequest.php @@ -117,6 +117,7 @@ class StoreTenantRequest extends FormRequest ], 'display_cart_item_images' => ['sometimes', 'boolean'], 'scanner_category_validation_enabled' => ['sometimes', 'boolean'], + 'allow_ticket_refund' => ['sometimes', 'boolean'], 'allow_ticket_total_refund' => ['sometimes', 'boolean'], 'allow_ticket_partial_refund' => ['sometimes', 'boolean'], 'ticket_partial_refund_percentage' => [ diff --git a/app/Domains/Tenant/Requests/UpdateTenantRequest.php b/app/Domains/Tenant/Requests/UpdateTenantRequest.php index 8c11cef..4acc110 100644 --- a/app/Domains/Tenant/Requests/UpdateTenantRequest.php +++ b/app/Domains/Tenant/Requests/UpdateTenantRequest.php @@ -138,6 +138,7 @@ class UpdateTenantRequest extends FormRequest ], 'display_cart_item_images' => ['sometimes', 'boolean'], 'scanner_category_validation_enabled' => ['sometimes', 'boolean'], + 'allow_ticket_refund' => ['sometimes', 'boolean'], 'allow_ticket_total_refund' => ['sometimes', 'boolean'], 'allow_ticket_partial_refund' => ['sometimes', 'boolean'], 'ticket_partial_refund_percentage' => [ diff --git a/app/Domains/Tenant/Resources/TenantResource.php b/app/Domains/Tenant/Resources/TenantResource.php index b005f40..1b94e48 100644 --- a/app/Domains/Tenant/Resources/TenantResource.php +++ b/app/Domains/Tenant/Resources/TenantResource.php @@ -87,6 +87,7 @@ class TenantResource extends JsonResource 'checkout_editing_policy' => CartEditingPolicyResource::make($this->checkout_editing_policy), 'display_cart_item_images' => $this->display_cart_item_images, 'scanner_category_validation_enabled' => $this->scanner_category_validation_enabled, + '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, diff --git a/app/Domains/Ticket/Services/AdminAppTicketService.php b/app/Domains/Ticket/Services/AdminAppTicketService.php index 3717500..46a1cde 100644 --- a/app/Domains/Ticket/Services/AdminAppTicketService.php +++ b/app/Domains/Ticket/Services/AdminAppTicketService.php @@ -141,12 +141,12 @@ class AdminAppTicketService $remainingItemAmount = max(0.0, round($itemTotal - $itemRefundedAmount, 2)); $total = null; - if ($tenant->allow_ticket_total_refund && $unitPrice <= $remainingItemAmount) { + if ($tenant->allow_refund() && $tenant->allow_ticket_total_refund && $unitPrice <= $remainingItemAmount) { $total = number_format($unitPrice, 2, '.', ''); } $partial = null; - if ($tenant->allow_partial_refund()) { + if ($tenant->allow_refund() && $tenant->allow_partial_refund()) { $partialAmount = $this->refundAmount($purchaseItem, $tenant, 'partial'); if ($partialAmount <= $remainingItemAmount) { $partial = number_format($partialAmount, 2, '.', ''); @@ -214,8 +214,8 @@ class AdminAppTicketService private function ensureRefundIsAllowed(Tenant $tenant, string $refundType): void { $isAllowed = match ($refundType) { - 'partial' => $tenant->allow_partial_refund(), - 'total' => (bool) $tenant->allow_ticket_total_refund, + 'partial' => $tenant->allow_refund() && $tenant->allow_partial_refund(), + 'total' => $tenant->allow_refund() && (bool) $tenant->allow_ticket_total_refund, }; if (! $isAllowed) { diff --git a/database/migrations/2026_09_11_010000_add_ticket_refund_master_toggle_to_tenants_table.php b/database/migrations/2026_09_11_010000_add_ticket_refund_master_toggle_to_tenants_table.php new file mode 100644 index 0000000..e831d5e --- /dev/null +++ b/database/migrations/2026_09_11_010000_add_ticket_refund_master_toggle_to_tenants_table.php @@ -0,0 +1,34 @@ +boolean('allow_ticket_refund') + ->default(false) + ->after('scanner_category_validation_enabled'); + }); + + DB::table('tenants') + ->where('allow_ticket_total_refund', true) + ->orWhere(function ($query): void { + $query + ->where('allow_ticket_partial_refund', true) + ->where('ticket_partial_refund_percentage', '>', 0); + }) + ->update(['allow_ticket_refund' => true]); + } + + public function down(): void + { + Schema::table('tenants', function (Blueprint $table): void { + $table->dropColumn('allow_ticket_refund'); + }); + } +}; diff --git a/tests/Feature/Tenant/TenantRefundConfigurationTest.php b/tests/Feature/Tenant/TenantRefundConfigurationTest.php index b50304a..6091bbb 100644 --- a/tests/Feature/Tenant/TenantRefundConfigurationTest.php +++ b/tests/Feature/Tenant/TenantRefundConfigurationTest.php @@ -18,12 +18,14 @@ class TenantRefundConfigurationTest extends TestCase { $tenant = $this->createTenant('refund-defaults'); + $this->assertFalse($tenant->allow_ticket_refund); $this->assertFalse($tenant->allow_ticket_total_refund); $this->assertFalse($tenant->allow_ticket_partial_refund); $this->assertSame('0.00', $tenant->ticket_partial_refund_percentage); $this->getJson("/api/tenants/{$tenant->codigo}") ->assertOk() + ->assertJsonPath('data.allow_ticket_refund', false) ->assertJsonPath('data.allow_ticket_total_refund', false) ->assertJsonPath('data.allow_ticket_partial_refund', false) ->assertJsonPath('data.ticket_partial_refund_percentage', '0.00'); @@ -34,17 +36,20 @@ class TenantRefundConfigurationTest extends TestCase $tenant = $this->createTenant('refund-update'); $this->putJson("/api/tenants/{$tenant->codigo}", [ + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => true, 'allow_ticket_partial_refund' => true, 'ticket_partial_refund_percentage' => 25.50, ]) ->assertOk() + ->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'); $this->assertDatabaseHas('tenants', [ 'id' => $tenant->id, + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => true, 'allow_ticket_partial_refund' => true, 'ticket_partial_refund_percentage' => 25.50, @@ -64,6 +69,7 @@ class TenantRefundConfigurationTest extends TestCase public function test_tenant_allow_refund_logic(): void { $tenant = new Tenant([ + 'allow_ticket_refund' => false, 'allow_ticket_total_refund' => false, 'allow_ticket_partial_refund' => false, 'ticket_partial_refund_percentage' => 0, @@ -83,6 +89,10 @@ class TenantRefundConfigurationTest extends TestCase // Partial refund enabled and percentage is set $tenant->ticket_partial_refund_percentage = 25.50; + $this->assertFalse($tenant->allow_refund()); + $this->assertFalse($tenant->allow_partial_refund()); + + $tenant->allow_ticket_refund = true; $this->assertTrue($tenant->allow_refund()); $this->assertTrue($tenant->allowRefund()); $this->assertTrue($tenant->allow_refund); @@ -101,6 +111,13 @@ class TenantRefundConfigurationTest extends TestCase $tenant->ticket_partial_refund_percentage = 50.00; $this->assertTrue($tenant->allow_refund()); $this->assertTrue($tenant->allow_partial_refund()); + + $tenant->allow_ticket_refund = false; + $this->assertFalse($tenant->allow_refund()); + $this->assertFalse($tenant->allow_partial_refund()); + $this->assertTrue($tenant->allow_ticket_total_refund); + $this->assertTrue($tenant->allow_ticket_partial_refund); + $this->assertSame('50.00', $tenant->ticket_partial_refund_percentage); } private function createTenant(string $code): Tenant diff --git a/tests/Feature/Ticket/AdminAppTicketControllerTest.php b/tests/Feature/Ticket/AdminAppTicketControllerTest.php index f7b5ee4..a933388 100644 --- a/tests/Feature/Ticket/AdminAppTicketControllerTest.php +++ b/tests/Feature/Ticket/AdminAppTicketControllerTest.php @@ -100,7 +100,10 @@ class AdminAppTicketControllerTest extends TestCase ->assertJsonPath('data.0.can_refund', false); // Total refund allowed - $tenant->update(['allow_ticket_total_refund' => true]); + $tenant->update([ + 'allow_ticket_refund' => true, + 'allow_ticket_total_refund' => true, + ]); $this->getJson('/api/v1/adminapp/tenant/tickets') ->assertOk() ->assertJsonPath('data.0.allow_refund', true) @@ -108,6 +111,7 @@ class AdminAppTicketControllerTest extends TestCase // Partial refund allowed with percentage set $tenant->update([ + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => false, 'allow_ticket_partial_refund' => true, 'ticket_partial_refund_percentage' => 20.00, @@ -117,6 +121,17 @@ class AdminAppTicketControllerTest extends TestCase ->assertJsonPath('data.0.allow_refund', true) ->assertJsonPath('data.0.can_refund', true); + // Master toggle disabled while preserving the partial refund preference + $tenant->update(['allow_ticket_refund' => false]); + $this->getJson('/api/v1/adminapp/tenant/tickets') + ->assertOk() + ->assertJsonPath('data.0.allow_refund', false) + ->assertJsonPath('data.0.can_refund', false); + $this->assertTrue($tenant->fresh()->allow_ticket_partial_refund); + $this->assertSame('20.00', $tenant->fresh()->ticket_partial_refund_percentage); + + $tenant->update(['allow_ticket_refund' => true]); + // Partial refund enabled but percentage is 0 $tenant->update([ 'ticket_partial_refund_percentage' => 0, @@ -162,7 +177,10 @@ class AdminAppTicketControllerTest extends TestCase public function test_it_totally_refunds_a_ticket_when_the_tenant_allows_it(): void { $tenant = $this->createTenant('ticket-total-refund'); - $tenant->update(['allow_ticket_total_refund' => true]); + $tenant->update([ + 'allow_ticket_refund' => true, + 'allow_ticket_total_refund' => true, + ]); $admin = $this->createAdminAppUser($tenant); $this->grantTicketsMenu($tenant); Sanctum::actingAs($admin); @@ -184,6 +202,7 @@ class AdminAppTicketControllerTest extends TestCase { $tenant = $this->createTenant('ticket-partial-refund'); $tenant->update([ + 'allow_ticket_refund' => true, 'allow_ticket_partial_refund' => true, 'ticket_partial_refund_percentage' => 25.50, ]); @@ -239,6 +258,7 @@ class AdminAppTicketControllerTest extends TestCase { $tenant = $this->createTenant('ticket-calc-both'); $tenant->update([ + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => true, 'allow_ticket_partial_refund' => true, 'ticket_partial_refund_percentage' => 30.00, @@ -258,6 +278,7 @@ class AdminAppTicketControllerTest extends TestCase { $tenant = $this->createTenant('ticket-calc-total'); $tenant->update([ + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => true, 'allow_ticket_partial_refund' => false, ]); @@ -276,6 +297,7 @@ class AdminAppTicketControllerTest extends TestCase { $tenant = $this->createTenant('ticket-calc-insufficient'); $tenant->update([ + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => true, 'allow_ticket_partial_refund' => true, 'ticket_partial_refund_percentage' => 50.00, @@ -299,7 +321,10 @@ class AdminAppTicketControllerTest extends TestCase public function test_it_fails_calculating_refund_if_ticket_is_not_active(): void { $tenant = $this->createTenant('ticket-calc-inactive'); - $tenant->update(['allow_ticket_total_refund' => true]); + $tenant->update([ + 'allow_ticket_refund' => true, + 'allow_ticket_total_refund' => true, + ]); $admin = $this->createAdminAppUser($tenant); $this->grantTicketsMenu($tenant); Sanctum::actingAs($admin); @@ -316,8 +341,14 @@ class AdminAppTicketControllerTest extends TestCase { $tenantA = $this->createTenant('ticket-calc-a'); $tenantB = $this->createTenant('ticket-calc-b'); - $tenantA->update(['allow_ticket_total_refund' => true]); - $tenantB->update(['allow_ticket_total_refund' => true]); + $tenantA->update([ + 'allow_ticket_refund' => true, + 'allow_ticket_total_refund' => true, + ]); + $tenantB->update([ + 'allow_ticket_refund' => true, + 'allow_ticket_total_refund' => true, + ]); $adminA = $this->createAdminAppUser($tenantA); $adminB = $this->createAdminAppUser($tenantB); diff --git a/tests/Unit/Ticket/TicketTest.php b/tests/Unit/Ticket/TicketTest.php index d473104..dc0b663 100644 --- a/tests/Unit/Ticket/TicketTest.php +++ b/tests/Unit/Ticket/TicketTest.php @@ -67,6 +67,7 @@ class TicketTest extends TestCase public function test_it_exposes_admin_action_capabilities(): void { $tenant = new Tenant([ + 'allow_ticket_refund' => true, 'allow_ticket_total_refund' => true, ]); $active = (new Ticket)->setRelation('tenant', $tenant);