feat: Implement user-specific payroll filtering and enforce authorization for payroll access and export.

This commit is contained in:
Yoga Pangestu 2025-12-18 09:54:11 +07:00
parent b66474ed3e
commit 078a541686
3 changed files with 25 additions and 6 deletions

View File

@ -105,6 +105,7 @@ public function builder(): Builder
'payrolls.created_at' 'payrolls.created_at'
) )
->paid() ->paid()
->forCurrentUser()
->with(['user', 'user.employee']); ->with(['user', 'user.employee']);
} }
} }

View File

@ -18,6 +18,7 @@
use App\Traits\Utilities\WithUpdatedData; use App\Traits\Utilities\WithUpdatedData;
use Barryvdh\DomPDF\Facade\Pdf; use Barryvdh\DomPDF\Facade\Pdf;
use Flux\Flux; use Flux\Flux;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Livewire\Attributes\On; use Livewire\Attributes\On;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -38,7 +39,7 @@ class Payroll extends Component
public array $users = []; public array $users = [];
public function mount() public function mount(): void
{ {
$this->loadPayrolls(); $this->loadPayrolls();
@ -51,10 +52,11 @@ public function mount()
->toArray(); ->toArray();
} }
private function loadPayrolls() private function loadPayrolls(): void
{ {
$this->payrolls = PayrollModel::with(['user', 'user.employee']) $this->payrolls = PayrollModel::with(['user', 'user.employee'])
->notPaid() ->notPaid()
->forCurrentUser()
->where('period_month', now()->format('Y-m')) ->where('period_month', now()->format('Y-m'))
->whereHas('user', function ($query) { ->whereHas('user', function ($query) {
$query->active() $query->active()
@ -64,7 +66,7 @@ private function loadPayrolls()
} }
#[On('modal:open')] #[On('modal:open')]
public function openModal(string $method, string $modalTitle, ?string $id = null) public function openModal(string $method, string $modalTitle, ?string $id = null): void
{ {
$this->resetValidation(); $this->resetValidation();
$this->resetErrorBag(); $this->resetErrorBag();
@ -73,7 +75,7 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
$this->modalTitle = $modalTitle; $this->modalTitle = $modalTitle;
} }
public function create() public function create(): void
{ {
$this->canOrAbort('manage adjustment'); $this->canOrAbort('manage adjustment');
@ -97,7 +99,7 @@ public function create()
Flux::modals()->close(); Flux::modals()->close();
} }
public function delete(PayrollAdjustment $adjustment) public function delete(PayrollAdjustment $adjustment): void
{ {
$this->canOrAbort('manage adjustment'); $this->canOrAbort('manage adjustment');
@ -129,6 +131,12 @@ public function exportPdf(PayrollModel $payroll)
{ {
$this->canOrAbort('view payroll'); $this->canOrAbort('view payroll');
// Validasi: user hanya bisa export payroll milik sendiri (kecuali Owner/Developer)
$user = auth()->user();
if (! $user->hasRole(['Owner', 'Developer']) && $payroll->user_id !== $user->id) {
abort(403, 'Anda tidak memiliki akses untuk melihat payroll ini.');
}
$payroll->load(['user.employee', 'adjustments']); $payroll->load(['user.employee', 'adjustments']);
$pdf = Pdf::loadView('pdf.payroll-slip', [ $pdf = Pdf::loadView('pdf.payroll-slip', [
@ -147,7 +155,7 @@ public function exportPdf(PayrollModel $payroll)
}, $fileName); }, $fileName);
} }
public function render() public function render(): View
{ {
return view('livewire.studio.finance.payrolls', [ return view('livewire.studio.finance.payrolls', [
'pageTitle' => 'Penggajian', 'pageTitle' => 'Penggajian',

View File

@ -44,6 +44,16 @@ public function notPaid(Builder $query): void
$query->where('is_paid', IsPaid::NOT_PAID->value); $query->where('is_paid', IsPaid::NOT_PAID->value);
} }
#[Scope]
public function forCurrentUser(Builder $query): void
{
$user = auth()->user();
if ($user && ! $user->hasRole(['Owner', 'Developer'])) {
$query->where('user_id', $user->id);
}
}
public function user(): BelongsTo public function user(): BelongsTo
{ {
return $this->belongsTo(User::class); return $this->belongsTo(User::class);