Skip to content

Commit 9f499d9

Browse files
committed
Fix ticket billing settings 404 by registering proper Gate authorization
The TicketBillingSettings Livewire component was incorrectly using $this->authorize() with a Policy class as second argument, which Laravel doesn't support for non-model policies. Changes: - Add defineTicketBillingGates() method to AuthServiceProvider with proper Gate definitions for all ticket billing permissions - Update TicketBillingSettings.php to use the new Gate names: - ticket-billing.view-settings - ticket-billing.manage-settings - ticket-billing.process-pending - ticket-billing.dry-run This fixes the 404 error when accessing /settings/ticket-billing
1 parent 7c117f2 commit 9f499d9

File tree

2 files changed

+110
-20
lines changed

2 files changed

+110
-20
lines changed

app/Livewire/Settings/TicketBillingSettings.php

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,45 @@
22

33
namespace App\Livewire\Settings;
44

5+
use Flux\Flux;
56
use Illuminate\Support\Facades\Artisan;
67
use Illuminate\Support\Facades\Cache;
78
use Illuminate\Support\Facades\Log;
89
use Livewire\Component;
9-
use Flux\Flux;
1010

1111
class TicketBillingSettings extends Component
1212
{
1313
// Configuration properties
1414
public bool $enabled;
15+
1516
public bool $autoBillOnClose;
17+
1618
public bool $autoBillOnResolve;
19+
1720
public string $defaultStrategy;
21+
1822
public float $minBillableHours;
23+
1924
public float $roundHoursTo;
25+
2026
public int $invoiceDueDays;
27+
2128
public bool $requireApproval;
29+
2230
public bool $skipZeroInvoices;
31+
2332
public bool $autoSend;
33+
2434
public int $batchSize;
2535

2636
// Statistics
2737
public int $pendingTicketsCount = 0;
38+
2839
public int $billingQueueCount = 0;
29-
40+
3041
// Processing
3142
public bool $processing = false;
43+
3244
public ?string $processingResult = null;
3345

3446
protected $rules = [
@@ -48,8 +60,8 @@ class TicketBillingSettings extends Component
4860
public function mount()
4961
{
5062
// Authorization check
51-
$this->authorize('viewSettings', \App\Policies\TicketBillingPolicy::class);
52-
63+
$this->authorize('ticket-billing.view-settings');
64+
5365
$this->loadConfiguration();
5466
$this->loadStatistics();
5567
}
@@ -86,16 +98,16 @@ protected function loadStatistics()
8698
->count();
8799
} catch (\Exception $e) {
88100
Log::error('Failed to load ticket billing statistics', [
89-
'error' => $e->getMessage()
101+
'error' => $e->getMessage(),
90102
]);
91103
}
92104
}
93105

94106
public function save()
95107
{
96108
// Authorization check
97-
$this->authorize('manageSettings', \App\Policies\TicketBillingPolicy::class);
98-
109+
$this->authorize('ticket-billing.manage-settings');
110+
99111
$this->validate();
100112

101113
try {
@@ -132,7 +144,7 @@ public function save()
132144
} catch (\Exception $e) {
133145
Flux::toast(
134146
variant: 'danger',
135-
text: 'Failed to save settings: ' . $e->getMessage(),
147+
text: 'Failed to save settings: '.$e->getMessage(),
136148
duration: 5000
137149
);
138150

@@ -146,8 +158,8 @@ public function save()
146158
public function processPendingTickets($limit = null)
147159
{
148160
// Authorization check
149-
$this->authorize('processPendingTickets', \App\Policies\TicketBillingPolicy::class);
150-
161+
$this->authorize('ticket-billing.process-pending');
162+
151163
$this->processing = true;
152164
$this->processingResult = null;
153165

@@ -182,7 +194,7 @@ public function processPendingTickets($limit = null)
182194
$this->processingResult = 'error';
183195
Flux::toast(
184196
variant: 'danger',
185-
text: 'Error processing tickets: ' . $e->getMessage(),
197+
text: 'Error processing tickets: '.$e->getMessage(),
186198
duration: 5000
187199
);
188200
} finally {
@@ -193,8 +205,8 @@ public function processPendingTickets($limit = null)
193205
public function testDryRun()
194206
{
195207
// Authorization check
196-
$this->authorize('runDryRun', \App\Policies\TicketBillingPolicy::class);
197-
208+
$this->authorize('ticket-billing.dry-run');
209+
198210
try {
199211
Artisan::call('billing:process-pending-tickets', [
200212
'--dry-run' => true,
@@ -212,7 +224,7 @@ public function testDryRun()
212224
} catch (\Exception $e) {
213225
Flux::toast(
214226
variant: 'danger',
215-
text: 'Dry run failed: ' . $e->getMessage(),
227+
text: 'Dry run failed: '.$e->getMessage(),
216228
duration: 5000
217229
);
218230
}
@@ -222,7 +234,7 @@ protected function updateEnvFile(array $data)
222234
{
223235
$envFile = base_path('.env');
224236

225-
if (!file_exists($envFile)) {
237+
if (! file_exists($envFile)) {
226238
throw new \Exception('.env file not found');
227239
}
228240

app/Providers/AuthServiceProvider.php

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ class AuthServiceProvider extends ServiceProvider
3939
\App\Domains\Product\Models\PricingRule::class => \App\Policies\PricingRulePolicy::class,
4040
\App\Domains\Contract\Models\ContractTemplate::class => \App\Policies\ContractTemplatePolicy::class,
4141
\App\Domains\Email\Models\EmailAccount::class => \App\Policies\EmailAccountPolicy::class,
42-
\App\Domains\HR\Models\EmployeeTimeEntry::class => \App\Policies\EmployeeTimeEntryPolicy::class,
43-
\App\Domains\Integration\Models\RmmIntegration::class => \App\Policies\RmmIntegrationPolicy::class,
44-
\App\Domains\Financial\Models\PlaidItem::class => \App\Policies\PlaidItemPolicy::class,
45-
\App\Domains\Financial\Models\BankTransaction::class => \App\Policies\BankTransactionPolicy::class,
46-
];
42+
\App\Domains\HR\Models\EmployeeTimeEntry::class => \App\Policies\EmployeeTimeEntryPolicy::class,
43+
\App\Domains\Integration\Models\RmmIntegration::class => \App\Policies\RmmIntegrationPolicy::class,
44+
\App\Domains\Financial\Models\PlaidItem::class => \App\Policies\PlaidItemPolicy::class,
45+
\App\Domains\Financial\Models\BankTransaction::class => \App\Policies\BankTransactionPolicy::class,
46+
\App\Domains\Client\Models\ClientDomain::class => \App\Policies\ClientDomainPolicy::class,
47+
];
4748

4849
/**
4950
* Register any authentication / authorization services.
@@ -84,6 +85,9 @@ public function boot(): void
8485

8586
// Define product management gates
8687
$this->defineProductGates();
88+
89+
// Define ticket billing gates
90+
$this->defineTicketBillingGates();
8791
}
8892

8993
/**
@@ -765,6 +769,80 @@ protected function defineProductGates(): void
765769
});
766770
}
767771

772+
/**
773+
* Define ticket billing authorization gates.
774+
*/
775+
protected function defineTicketBillingGates(): void
776+
{
777+
// View ticket billing settings
778+
Gate::define('ticket-billing.view-settings', function (User $user) {
779+
return $user->hasPermission('billing.settings.view')
780+
|| $user->hasPermission('billing.settings.manage')
781+
|| $user->isAdmin();
782+
});
783+
784+
// Manage ticket billing settings
785+
Gate::define('ticket-billing.manage-settings', function (User $user) {
786+
return $user->hasPermission('billing.settings.manage')
787+
|| $user->isAdmin();
788+
});
789+
790+
// Process pending tickets for billing
791+
Gate::define('ticket-billing.process-pending', function (User $user) {
792+
return $user->hasPermission('billing.tickets.process')
793+
|| $user->hasPermission('billing.settings.manage')
794+
|| $user->isAdmin();
795+
});
796+
797+
// Run dry-run billing previews
798+
Gate::define('ticket-billing.dry-run', function (User $user) {
799+
return $user->hasPermission('billing.tickets.process')
800+
|| $user->hasPermission('billing.settings.view')
801+
|| $user->hasRole('manager')
802+
|| $user->isAdmin();
803+
});
804+
805+
// Generate invoice from ticket
806+
Gate::define('ticket-billing.generate-invoice', function (User $user) {
807+
return $user->hasPermission('billing.tickets.generate')
808+
|| $user->isAdmin();
809+
});
810+
811+
// Approve billing invoices
812+
Gate::define('ticket-billing.approve-invoice', function (User $user) {
813+
return $user->hasPermission('billing.tickets.approve')
814+
|| $user->hasRole('manager')
815+
|| $user->isAdmin();
816+
});
817+
818+
// Void/cancel billing invoices
819+
Gate::define('ticket-billing.void-invoice', function (User $user) {
820+
return $user->hasPermission('billing.tickets.void')
821+
|| $user->hasRole('manager')
822+
|| $user->isAdmin();
823+
});
824+
825+
// View billing reports
826+
Gate::define('ticket-billing.view-reports', function (User $user) {
827+
return $user->hasPermission('billing.reports.view')
828+
|| $user->hasRole('manager')
829+
|| $user->isAdmin();
830+
});
831+
832+
// Modify billing amounts
833+
Gate::define('ticket-billing.modify-amount', function (User $user) {
834+
return $user->hasPermission('billing.tickets.modify')
835+
|| $user->isAdmin();
836+
});
837+
838+
// View billing audit logs
839+
Gate::define('ticket-billing.view-audit-logs', function (User $user) {
840+
return $user->hasPermission('billing.audit.view')
841+
|| $user->hasRole('manager')
842+
|| $user->isAdmin();
843+
});
844+
}
845+
768846
/**
769847
* Get all available permissions grouped by category.
770848
*/

0 commit comments

Comments
 (0)