200 lines
7.3 KiB
PHP
200 lines
7.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Branch;
|
|
use App\Models\ThirteenthMonthRun;
|
|
use App\Models\ThirteenthMonthEntry;
|
|
use App\Services\ThirteenthMonthService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Inertia\Inertia;
|
|
|
|
class ThirteenthMonthController extends Controller
|
|
{
|
|
protected $service;
|
|
|
|
public function __construct(ThirteenthMonthService $service)
|
|
{
|
|
$this->service = $service;
|
|
}
|
|
|
|
/**
|
|
* Display the 13th Month Pay management index page.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$currentYear = (int) date('Y');
|
|
|
|
$startYear = $request->input('start_year') ? (int) $request->input('start_year') : $currentYear;
|
|
$startMonth = $request->input('start_month') ? (int) $request->input('start_month') : 1;
|
|
$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;
|
|
|
|
// Available years for back-year filtering (last 5 years up to next year)
|
|
$availableYears = range($currentYear - 5, $currentYear + 1);
|
|
$branches = Branch::all(['id', 'name']);
|
|
|
|
// Check if an existing run matches these range parameters
|
|
$existingRun = ThirteenthMonthRun::where('start_year', $startYear)
|
|
->where('start_month', $startMonth)
|
|
->where('end_year', $endYear)
|
|
->where('end_month', $endMonth)
|
|
->when($selectedBranchId, function ($q) use ($selectedBranchId) {
|
|
$q->where('branch_id', $selectedBranchId);
|
|
}, function ($q) {
|
|
$q->whereNull('branch_id');
|
|
})
|
|
->with(['entries.employee.user', 'entries.employee.designation', 'entries.employee.department', 'branch', 'creator'])
|
|
->first();
|
|
|
|
if (!$existingRun) {
|
|
// Generate dynamic run preview on the fly
|
|
$existingRun = $this->service->generateRun($startYear, $startMonth, $endYear, $endMonth, $selectedBranchId, null, Auth::id());
|
|
}
|
|
|
|
// Fetch historical runs list
|
|
$pastRuns = ThirteenthMonthRun::with(['branch', 'creator'])
|
|
->orderBy('year', 'desc')
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
return Inertia::render('hr/thirteenth-month/index', [
|
|
'activeRun' => $existingRun,
|
|
'pastRuns' => $pastRuns,
|
|
'branches' => $branches,
|
|
'availableYears' => array_values($availableYears),
|
|
'filters' => [
|
|
'start_year' => $startYear,
|
|
'start_month' => $startMonth,
|
|
'end_year' => $endYear,
|
|
'end_month' => $endMonth,
|
|
'branch_id' => $selectedBranchId,
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generate or recalculate a 13th month batch run with custom date ranges.
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$request->validate([
|
|
'start_year' => 'required|integer|min:2020|max:2035',
|
|
'start_month' => 'required|integer|min:1|max:12',
|
|
'end_year' => 'required|integer|min:2020|max:2035',
|
|
'end_month' => 'required|integer|min:1|max:12',
|
|
'branch_id' => 'nullable|exists:branches,id',
|
|
]);
|
|
|
|
$startYear = (int) $request->start_year;
|
|
$startMonth = (int) $request->start_month;
|
|
$endYear = (int) $request->end_year;
|
|
$endMonth = (int) $request->end_month;
|
|
$branchId = $request->branch_id ? (int) $request->branch_id : null;
|
|
|
|
$run = $this->service->generateRun($startYear, $startMonth, $endYear, $endMonth, $branchId, null, Auth::id());
|
|
|
|
return redirect()->back()->with('success', '13th Month Pay range calculated successfully!');
|
|
}
|
|
|
|
/**
|
|
* Update an individual employee's adjustment, monthly earnings breakdown, or notes.
|
|
*/
|
|
public function updateEntry(Request $request, $id)
|
|
{
|
|
$request->validate([
|
|
'adjustment_amount' => 'nullable|numeric',
|
|
'monthly_earnings' => 'nullable|array',
|
|
'notes' => 'nullable|string|max:255',
|
|
]);
|
|
|
|
$entry = ThirteenthMonthEntry::findOrFail($id);
|
|
|
|
if ($request->has('adjustment_amount')) {
|
|
$entry->adjustment_amount = (float) $request->adjustment_amount;
|
|
}
|
|
|
|
if ($request->has('monthly_earnings')) {
|
|
$monthlyEarnings = $request->monthly_earnings;
|
|
$entry->monthly_earnings = $monthlyEarnings;
|
|
|
|
// Recalculate employee total base earned, computed amount, and final payout
|
|
$totalBase = array_sum(array_map('floatval', $monthlyEarnings));
|
|
$entry->total_base_earned = round($totalBase, 2);
|
|
$entry->computed_amount = round($totalBase / 12, 2);
|
|
}
|
|
|
|
$entry->final_payout = round($entry->computed_amount + $entry->adjustment_amount, 2);
|
|
|
|
if ($request->has('notes')) {
|
|
$entry->notes = $request->notes;
|
|
}
|
|
|
|
$entry->save();
|
|
|
|
// Recalculate parent run totals
|
|
$run = $entry->run;
|
|
if ($run) {
|
|
$run->total_base_earnings = round($run->entries()->sum('total_base_earned'), 2);
|
|
$run->total_payout = round($run->entries()->sum('final_payout'), 2);
|
|
$run->save();
|
|
}
|
|
|
|
return redirect()->back()->with('success', '13th Month entry updated successfully.');
|
|
}
|
|
|
|
/**
|
|
* Approve and lock the 13th Month Pay run.
|
|
*/
|
|
public function approveRun(Request $request, $id)
|
|
{
|
|
$run = ThirteenthMonthRun::findOrFail($id);
|
|
$run->status = 'approved';
|
|
$run->save();
|
|
|
|
return redirect()->back()->with('success', '13th Month Pay run has been approved!');
|
|
}
|
|
|
|
/**
|
|
* Export 13th Month Payout Schedule as Bank CSV.
|
|
*/
|
|
public function exportCsv($id)
|
|
{
|
|
$run = ThirteenthMonthRun::with(['entries.employee.user', 'entries.employee.department', 'branch'])->findOrFail($id);
|
|
|
|
$filename = "13th_Month_Pay_{$run->start_year}_{$run->start_month}_to_{$run->end_year}_{$run->end_month}_Export.csv";
|
|
$headers = [
|
|
'Content-Type' => 'text/csv',
|
|
'Content-Disposition' => "attachment; filename=\"{$filename}\"",
|
|
];
|
|
|
|
$callback = function () use ($run) {
|
|
$file = fopen('php://output', 'w');
|
|
fputcsv($file, ['Employee Code', 'Employee Name', 'Department', 'Total Base Earned', 'Computed 13th Month', 'Adjustment', 'Final Net Payout']);
|
|
|
|
foreach ($run->entries as $entry) {
|
|
$empCode = $entry->employee->employee_code ?? "EMP-{$entry->employee_id}";
|
|
$empName = $entry->employee->user->name ?? 'Staff';
|
|
$dept = $entry->employee->department->name ?? 'General';
|
|
|
|
fputcsv($file, [
|
|
$empCode,
|
|
$empName,
|
|
$dept,
|
|
number_format($entry->total_base_earned, 2, '.', ''),
|
|
number_format($entry->computed_amount, 2, '.', ''),
|
|
number_format($entry->adjustment_amount, 2, '.', ''),
|
|
number_format($entry->final_payout, 2, '.', ''),
|
|
]);
|
|
}
|
|
|
|
fclose($file);
|
|
};
|
|
|
|
return response()->stream($callback, 200, $headers);
|
|
}
|
|
}
|