refactor: implement strict role-based access control and scoped data visibility for finance and HR operations
This commit is contained in:
parent
36124c6535
commit
1de9d32136
@ -30,8 +30,8 @@ public function index(Request $request): Response
|
|||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
return Inertia::render('admin/finance/employee-advances/Index', [
|
return Inertia::render('admin/finance/employee-advances/Index', [
|
||||||
'employeeAdvances' => $this->employeeAdvanceService->paginateForIndex($tableQuery),
|
'employeeAdvances' => $this->employeeAdvanceService->paginateForIndex($tableQuery, $user),
|
||||||
'summary' => $this->employeeAdvanceService->outstandingSummary(),
|
'summary' => $this->employeeAdvanceService->outstandingSummary($user),
|
||||||
'authEmployeeId' => $user?->employee?->id,
|
'authEmployeeId' => $user?->employee?->id,
|
||||||
'canSubmit' => $user?->can(Permission::EMPLOYEE_ADVANCES_CREATE->value)
|
'canSubmit' => $user?->can(Permission::EMPLOYEE_ADVANCES_CREATE->value)
|
||||||
&& ! $user->can(Permission::EMPLOYEE_ADVANCES_VERIFY->value)
|
&& ! $user->can(Permission::EMPLOYEE_ADVANCES_VERIFY->value)
|
||||||
@ -71,7 +71,12 @@ public function destroy(EmployeeAdvance $employeeAdvance): RedirectResponse
|
|||||||
|
|
||||||
public function approve(EmployeeAdvance $employeeAdvance): RedirectResponse
|
public function approve(EmployeeAdvance $employeeAdvance): RedirectResponse
|
||||||
{
|
{
|
||||||
$this->employeeAdvanceService->approve($employeeAdvance, auth()->user());
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->employeeAdvanceService->approve($employeeAdvance, $user);
|
||||||
|
|
||||||
$this->flashSuccess('Kasbon berhasil disetujui dan dicairkan dari kas.');
|
$this->flashSuccess('Kasbon berhasil disetujui dan dicairkan dari kas.');
|
||||||
|
|
||||||
@ -80,10 +85,15 @@ public function approve(EmployeeAdvance $employeeAdvance): RedirectResponse
|
|||||||
|
|
||||||
public function reject(RejectEmployeeAdvanceRequest $request, EmployeeAdvance $employeeAdvance): RedirectResponse
|
public function reject(RejectEmployeeAdvanceRequest $request, EmployeeAdvance $employeeAdvance): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
$this->employeeAdvanceService->reject(
|
$this->employeeAdvanceService->reject(
|
||||||
$employeeAdvance,
|
$employeeAdvance,
|
||||||
$request->validated('reason'),
|
$request->validated('reason'),
|
||||||
auth()->user(),
|
$user,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->flashSuccess('Kasbon berhasil ditolak.');
|
$this->flashSuccess('Kasbon berhasil ditolak.');
|
||||||
@ -93,7 +103,12 @@ public function reject(RejectEmployeeAdvanceRequest $request, EmployeeAdvance $e
|
|||||||
|
|
||||||
public function pay(EmployeeAdvance $employeeAdvance): RedirectResponse
|
public function pay(EmployeeAdvance $employeeAdvance): RedirectResponse
|
||||||
{
|
{
|
||||||
$this->employeeAdvanceService->pay($employeeAdvance, auth()->user());
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->employeeAdvanceService->pay($employeeAdvance, $user);
|
||||||
|
|
||||||
$this->flashSuccess('Kasbon berhasil dilunasi.');
|
$this->flashSuccess('Kasbon berhasil dilunasi.');
|
||||||
|
|
||||||
|
|||||||
@ -20,10 +20,15 @@ public function __construct(
|
|||||||
|
|
||||||
public function storeAdjustment(PayrollAdjustmentRequest $request, Payroll $payroll): RedirectResponse
|
public function storeAdjustment(PayrollAdjustmentRequest $request, Payroll $payroll): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses untuk menambah penyesuaian gaji.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->payrollService->addAdjustment(
|
$this->payrollService->addAdjustment(
|
||||||
$payroll,
|
$payroll,
|
||||||
$request->validated(),
|
$request->validated(),
|
||||||
$request->user(),
|
$user,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->flashSuccess('Penyesuaian gaji berhasil ditambahkan.');
|
$this->flashSuccess('Penyesuaian gaji berhasil ditambahkan.');
|
||||||
@ -35,10 +40,15 @@ public function storeAdjustment(PayrollAdjustmentRequest $request, Payroll $payr
|
|||||||
|
|
||||||
public function updateAdjustment(PayrollAdjustmentRequest $request, PayrollAdjustment $payrollAdjustment): RedirectResponse
|
public function updateAdjustment(PayrollAdjustmentRequest $request, PayrollAdjustment $payrollAdjustment): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses untuk mengubah penyesuaian gaji.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->payrollService->updateAdjustment(
|
$this->payrollService->updateAdjustment(
|
||||||
$payrollAdjustment,
|
$payrollAdjustment,
|
||||||
$request->validated(),
|
$request->validated(),
|
||||||
$request->user(),
|
$user,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->flashSuccess('Penyesuaian gaji berhasil diperbarui.');
|
$this->flashSuccess('Penyesuaian gaji berhasil diperbarui.');
|
||||||
@ -50,6 +60,11 @@ public function updateAdjustment(PayrollAdjustmentRequest $request, PayrollAdjus
|
|||||||
|
|
||||||
public function destroyAdjustment(PayrollAdjustment $payrollAdjustment): RedirectResponse
|
public function destroyAdjustment(PayrollAdjustment $payrollAdjustment): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses untuk menghapus penyesuaian gaji.');
|
||||||
|
}
|
||||||
|
|
||||||
$payroll = $payrollAdjustment->payroll;
|
$payroll = $payrollAdjustment->payroll;
|
||||||
|
|
||||||
$this->payrollService->deleteAdjustment($payrollAdjustment);
|
$this->payrollService->deleteAdjustment($payrollAdjustment);
|
||||||
|
|||||||
@ -25,15 +25,16 @@ public function index(Request $request): Response
|
|||||||
$tableQuery = $this->parseDataTableQuery($request);
|
$tableQuery = $this->parseDataTableQuery($request);
|
||||||
$periodId = $request->integer('period_id') ?: null;
|
$periodId = $request->integer('period_id') ?: null;
|
||||||
$period = $this->payrollService->resolvePeriod($periodId);
|
$period = $this->payrollService->resolvePeriod($periodId);
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
return Inertia::render('admin/finance/payroll/Index', [
|
return Inertia::render('admin/finance/payroll/Index', [
|
||||||
'periods' => $this->payrollService->listPeriods(),
|
'periods' => $this->payrollService->listPeriods(),
|
||||||
'currentPeriod' => $period,
|
'currentPeriod' => $period,
|
||||||
'payrolls' => $period
|
'payrolls' => $period
|
||||||
? $this->payrollService->paginateForPeriod($period, $tableQuery)
|
? $this->payrollService->paginateForPeriod($period, $tableQuery, $user)
|
||||||
: null,
|
: null,
|
||||||
'summary' => $period
|
'summary' => $period
|
||||||
? $this->payrollService->periodSummary($period)
|
? $this->payrollService->periodSummary($period, $user)
|
||||||
: null,
|
: null,
|
||||||
'adjustmentTypes' => PayrollAdjustmentType::selectOptions(),
|
'adjustmentTypes' => PayrollAdjustmentType::selectOptions(),
|
||||||
'filters' => array_merge(
|
'filters' => array_merge(
|
||||||
|
|||||||
@ -26,10 +26,10 @@ public function index(Request $request): Response
|
|||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
$employee = $user?->employee;
|
$employee = $user?->employee;
|
||||||
$canManageAll = $user?->can(Permission::ATTENDANCES_MANAGE->value) ?? false;
|
$isManager = $user?->hasAnyRole(['owner', 'developer', 'direktur']) ?? false;
|
||||||
|
|
||||||
$scopedEmployeeId = $canManageAll ? null : $employee?->id;
|
$scopedEmployeeId = $isManager ? null : $employee?->id;
|
||||||
$hasScopedAccess = $canManageAll || $employee !== null;
|
$hasScopedAccess = $isManager || $employee !== null;
|
||||||
|
|
||||||
$start = $request->date('start') ?? now()->startOfMonth();
|
$start = $request->date('start') ?? now()->startOfMonth();
|
||||||
$end = $request->date('end') ?? now()->endOfMonth();
|
$end = $request->date('end') ?? now()->endOfMonth();
|
||||||
@ -46,7 +46,7 @@ public function index(Request $request): Response
|
|||||||
: null,
|
: null,
|
||||||
'canCheckIn' => ($user?->can(Permission::ATTENDANCES_CREATE->value) ?? false)
|
'canCheckIn' => ($user?->can(Permission::ATTENDANCES_CREATE->value) ?? false)
|
||||||
&& $employee !== null,
|
&& $employee !== null,
|
||||||
'canManageAll' => $canManageAll,
|
'canManageAll' => $isManager,
|
||||||
'calendarRange' => [
|
'calendarRange' => [
|
||||||
'start' => $start->toDateString(),
|
'start' => $start->toDateString(),
|
||||||
'end' => $end->toDateString(),
|
'end' => $end->toDateString(),
|
||||||
@ -74,6 +74,13 @@ public function checkOut(AttendanceCheckOutRequest $request): RedirectResponse
|
|||||||
|
|
||||||
public function destroy(Attendance $attendance): RedirectResponse
|
public function destroy(Attendance $attendance): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
if ($attendance->employee_id !== $user->employee?->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses untuk menghapus data presensi ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->attendanceService->delete($attendance);
|
$this->attendanceService->delete($attendance);
|
||||||
|
|
||||||
$this->flashDeleted('Data presensi');
|
$this->flashDeleted('Data presensi');
|
||||||
|
|||||||
@ -39,7 +39,7 @@ public function index(Request $request): Response
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Inertia::render('admin/hr/leave-requests/Index', [
|
return Inertia::render('admin/hr/leave-requests/Index', [
|
||||||
'leaveRequests' => $this->leaveRequestService->paginateForIndex($tableQuery),
|
'leaveRequests' => $this->leaveRequestService->paginateForIndex($tableQuery, $user),
|
||||||
'authEmployeeId' => $user?->employee?->id,
|
'authEmployeeId' => $user?->employee?->id,
|
||||||
'canSubmit' => $user?->can(Permission::LEAVE_REQUESTS_CREATE->value)
|
'canSubmit' => $user?->can(Permission::LEAVE_REQUESTS_CREATE->value)
|
||||||
&& ! $user->can(Permission::LEAVE_REQUESTS_VERIFY->value)
|
&& ! $user->can(Permission::LEAVE_REQUESTS_VERIFY->value)
|
||||||
@ -80,7 +80,12 @@ public function destroy(LeaveRequest $leaveRequest): RedirectResponse
|
|||||||
|
|
||||||
public function approve(LeaveRequest $leaveRequest): RedirectResponse
|
public function approve(LeaveRequest $leaveRequest): RedirectResponse
|
||||||
{
|
{
|
||||||
$this->leaveRequestService->approve($leaveRequest, auth()->user());
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->leaveRequestService->approve($leaveRequest, $user);
|
||||||
|
|
||||||
$this->flashSuccess('Pengajuan cuti berhasil disetujui.');
|
$this->flashSuccess('Pengajuan cuti berhasil disetujui.');
|
||||||
|
|
||||||
@ -89,10 +94,15 @@ public function approve(LeaveRequest $leaveRequest): RedirectResponse
|
|||||||
|
|
||||||
public function reject(RejectLeaveRequestRequest $request, LeaveRequest $leaveRequest): RedirectResponse
|
public function reject(RejectLeaveRequestRequest $request, LeaveRequest $leaveRequest): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
$this->leaveRequestService->reject(
|
$this->leaveRequestService->reject(
|
||||||
$leaveRequest,
|
$leaveRequest,
|
||||||
$request->validated('reason'),
|
$request->validated('reason'),
|
||||||
auth()->user(),
|
$user,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->flashSuccess('Pengajuan cuti berhasil ditolak.');
|
$this->flashSuccess('Pengajuan cuti berhasil ditolak.');
|
||||||
|
|||||||
@ -26,15 +26,19 @@ public function __construct(
|
|||||||
/**
|
/**
|
||||||
* @return array{outstanding_amount: int, outstanding_amount_formatted: string, outstanding_count: int}
|
* @return array{outstanding_amount: int, outstanding_amount_formatted: string, outstanding_count: int}
|
||||||
*/
|
*/
|
||||||
public function outstandingSummary(): array
|
public function outstandingSummary(?User $user = null): array
|
||||||
{
|
{
|
||||||
$outstandingAmount = (int) EmployeeAdvance::query()
|
$query = EmployeeAdvance::query()
|
||||||
->where('status', EmployeeAdvanceStatus::APPROVED)
|
->where('status', EmployeeAdvanceStatus::APPROVED);
|
||||||
->sum('amount');
|
|
||||||
|
|
||||||
$outstandingCount = EmployeeAdvance::query()
|
if ($user !== null && ! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
->where('status', EmployeeAdvanceStatus::APPROVED)
|
$employeeId = $user->employee?->id ?? -1;
|
||||||
->count();
|
$query->where('employee_id', $employeeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$outstandingAmount = (int) $query->sum('amount');
|
||||||
|
|
||||||
|
$outstandingCount = $query->count();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'outstanding_amount' => $outstandingAmount,
|
'outstanding_amount' => $outstandingAmount,
|
||||||
@ -46,10 +50,14 @@ public function outstandingSummary(): array
|
|||||||
/**
|
/**
|
||||||
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
||||||
*/
|
*/
|
||||||
public function paginateForIndex(array $tableQuery): LengthAwarePaginator
|
public function paginateForIndex(array $tableQuery, User $user): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
$query = EmployeeAdvance::query()
|
$query = EmployeeAdvance::query()
|
||||||
->with(['employee.user.profile', 'rejection'])
|
->with(['employee.user.profile', 'rejection'])
|
||||||
|
->when(! $user->hasAnyRole(['owner', 'developer', 'direktur']), function (Builder $query) use ($user): void {
|
||||||
|
$employeeId = $user->employee?->id ?? -1;
|
||||||
|
$query->where('employee_id', $employeeId);
|
||||||
|
})
|
||||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||||
$search = $tableQuery['search'];
|
$search = $tableQuery['search'];
|
||||||
$query->where(function (Builder $query) use ($search): void {
|
$query->where(function (Builder $query) use ($search): void {
|
||||||
|
|||||||
@ -59,15 +59,19 @@ public function resolvePeriod(?int $periodId): ?PayrollPeriod
|
|||||||
/**
|
/**
|
||||||
* @return array{total_amount: int, total_amount_formatted: string, total_count: int, status: string, status_label: string}
|
* @return array{total_amount: int, total_amount_formatted: string, total_count: int, status: string, status_label: string}
|
||||||
*/
|
*/
|
||||||
public function periodSummary(PayrollPeriod $period): array
|
public function periodSummary(PayrollPeriod $period, User $user): array
|
||||||
{
|
{
|
||||||
$totalAmount = (int) Payroll::query()
|
$query = Payroll::query()
|
||||||
->where('payroll_period_id', $period->id)
|
->where('payroll_period_id', $period->id);
|
||||||
->sum('total_amount');
|
|
||||||
|
|
||||||
$totalCount = Payroll::query()
|
if (! $user->hasAnyRole(['owner', 'developer', 'direktur'])) {
|
||||||
->where('payroll_period_id', $period->id)
|
$employeeId = $user->employee?->id ?? -1;
|
||||||
->count();
|
$query->where('employee_id', $employeeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalAmount = (int) $query->sum('total_amount');
|
||||||
|
|
||||||
|
$totalCount = $query->count();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'total_amount' => $totalAmount,
|
'total_amount' => $totalAmount,
|
||||||
@ -81,11 +85,15 @@ public function periodSummary(PayrollPeriod $period): array
|
|||||||
/**
|
/**
|
||||||
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
||||||
*/
|
*/
|
||||||
public function paginateForPeriod(PayrollPeriod $period, array $tableQuery): LengthAwarePaginator
|
public function paginateForPeriod(PayrollPeriod $period, array $tableQuery, User $user): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
$query = Payroll::query()
|
$query = Payroll::query()
|
||||||
->with(['employee.user.profile', 'payrollPeriod', 'adjustments.createdBy.profile'])
|
->with(['employee.user.profile', 'payrollPeriod', 'adjustments.createdBy.profile'])
|
||||||
->where('payroll_period_id', $period->id)
|
->where('payroll_period_id', $period->id)
|
||||||
|
->when(! $user->hasAnyRole(['owner', 'developer', 'direktur']), function (Builder $query) use ($user): void {
|
||||||
|
$employeeId = $user->employee?->id ?? -1;
|
||||||
|
$query->where('employee_id', $employeeId);
|
||||||
|
})
|
||||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||||
$search = $tableQuery['search'];
|
$search = $tableQuery['search'];
|
||||||
$query->where(function (Builder $query) use ($search): void {
|
$query->where(function (Builder $query) use ($search): void {
|
||||||
|
|||||||
@ -24,10 +24,14 @@ public function __construct(
|
|||||||
/**
|
/**
|
||||||
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
||||||
*/
|
*/
|
||||||
public function paginateForIndex(array $tableQuery): LengthAwarePaginator
|
public function paginateForIndex(array $tableQuery, User $user): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
$query = LeaveRequest::query()
|
$query = LeaveRequest::query()
|
||||||
->with(['employee.user.profile', 'rejection'])
|
->with(['employee.user.profile', 'rejection'])
|
||||||
|
->when(! $user->hasAnyRole(['owner', 'developer', 'direktur']), function (Builder $query) use ($user): void {
|
||||||
|
$employeeId = $user->employee?->id ?? -1;
|
||||||
|
$query->where('employee_id', $employeeId);
|
||||||
|
})
|
||||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||||
$search = $tableQuery['search'];
|
$search = $tableQuery['search'];
|
||||||
$query->where(function (Builder $query) use ($search): void {
|
$query->where(function (Builder $query) use ($search): void {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user