feat(auth): enforce strict branch scoping for overtime requests, attendance regularizations, and 13th month pay

- Restrict overtime applications query, submission, approval, and attendance details to assigned branch
- Restrict attendance regularization listing, statistics, and dropdowns to assigned branch
- Restrict 13th month pay batch generation, historical runs, entry updates, approvals, and CSV exports to assigned branch
- Add feature tests in BranchScopedAccessTest for overtime and 13th month isolation
This commit is contained in:
2026-09-10 11:22:15 +08:00
parent 7f467600e9
commit 33f776c6b2
4 changed files with 251 additions and 20 deletions

View File

@@ -31,11 +31,16 @@ class ThirteenthMonthController extends Controller
$endYear = $request->input('end_year') ? (int) $request->input('end_year') : $currentYear;
$endMonth = $request->input('end_month') ? (int) $request->input('end_month') : 12;
$selectedBranchId = $request->input('branch_id') ? (int) $request->input('branch_id') : null;
$scopedBranchId = authBranchId();
$selectedBranchId = $scopedBranchId ?: ($request->input('branch_id') ? (int) $request->input('branch_id') : null);
// Available years for back-year filtering (last 5 years up to next year)
$availableYears = range($currentYear - 5, $currentYear + 1);
$branches = Branch::all(['id', 'name']);
$branchesQuery = Branch::query();
if ($scopedBranchId) {
$branchesQuery->where('id', $scopedBranchId);
}
$branches = $branchesQuery->get(['id', 'name']);
// Check if an existing run matches these range parameters
$existingRun = ThirteenthMonthRun::where('start_year', $startYear)
@@ -56,10 +61,15 @@ class ThirteenthMonthController extends Controller
}
// Fetch historical runs list
$pastRuns = ThirteenthMonthRun::with(['branch', 'creator'])
$pastRunsQuery = ThirteenthMonthRun::with(['branch', 'creator'])
->orderBy('year', 'desc')
->orderBy('created_at', 'desc')
->get();
->orderBy('created_at', 'desc');
if ($scopedBranchId) {
$pastRunsQuery->where('branch_id', $scopedBranchId);
}
$pastRuns = $pastRunsQuery->get();
return Inertia::render('hr/thirteenth-month/index', [
'activeRun' => $existingRun,
@@ -93,7 +103,9 @@ class ThirteenthMonthController extends Controller
$startMonth = (int) $request->start_month;
$endYear = (int) $request->end_year;
$endMonth = (int) $request->end_month;
$branchId = $request->branch_id ? (int) $request->branch_id : null;
$scopedBranchId = authBranchId();
$branchId = $scopedBranchId ?: ($request->branch_id ? (int) $request->branch_id : null);
$run = $this->service->generateRun($startYear, $startMonth, $endYear, $endMonth, $branchId, null, Auth::id());
@@ -111,7 +123,14 @@ class ThirteenthMonthController extends Controller
'notes' => 'nullable|string|max:255',
]);
$entry = ThirteenthMonthEntry::findOrFail($id);
$entry = ThirteenthMonthEntry::with(['employee', 'run'])->findOrFail($id);
$scopedBranchId = authBranchId();
if ($scopedBranchId) {
$empBranchId = $entry->employee?->branch_id ?? $entry->run?->branch_id;
if ($empBranchId && (int)$empBranchId !== (int)$scopedBranchId) {
abort(403, 'Unauthorized to update 13th Month entry for an employee outside your assigned branch.');
}
}
if ($request->has('adjustment_amount')) {
$entry->adjustment_amount = (float) $request->adjustment_amount;
@@ -152,6 +171,11 @@ class ThirteenthMonthController extends Controller
public function approveRun(Request $request, $id)
{
$run = ThirteenthMonthRun::findOrFail($id);
$scopedBranchId = authBranchId();
if ($scopedBranchId && $run->branch_id && (int)$run->branch_id !== (int)$scopedBranchId) {
abort(403, 'Unauthorized to approve 13th Month run for another branch.');
}
$run->status = 'approved';
$run->save();
@@ -164,6 +188,10 @@ class ThirteenthMonthController extends Controller
public function exportCsv($id)
{
$run = ThirteenthMonthRun::with(['entries.employee.user', 'entries.employee.department', 'branch'])->findOrFail($id);
$scopedBranchId = authBranchId();
if ($scopedBranchId && $run->branch_id && (int)$run->branch_id !== (int)$scopedBranchId) {
abort(403, 'Unauthorized to export 13th Month run for another branch.');
}
$filename = "13th_Month_Pay_{$run->start_year}_{$run->start_month}_to_{$run->end_year}_{$run->end_month}_Export.csv";
$headers = [