121 lines
4.2 KiB
PHP
121 lines
4.2 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\Employee;
|
||
use App\Models\Payroll;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Validation\Rule;
|
||
use Inertia\Inertia;
|
||
|
||
class PayrollController extends Controller
|
||
{
|
||
public function index()
|
||
{
|
||
// Eager loading untuk mencegah N+1 query
|
||
$payrolls = Payroll::with('employee')
|
||
->latest()
|
||
->get()
|
||
->map(fn($p) => [
|
||
'id' => $p->id,
|
||
'period' => $p->period,
|
||
'net_salary' => $p->net_salary,
|
||
'status' => $p->status,
|
||
'employee_name' => $p->employee?->name ?? '-',
|
||
'employee_nip' => $p->employee?->nip ?? '-',
|
||
]);
|
||
|
||
return Inertia::render('admin/payrolls/index', [
|
||
'payrolls' => $payrolls,
|
||
]);
|
||
}
|
||
|
||
public function create()
|
||
{
|
||
$employees = Employee::with(['position', 'department'])
|
||
->orderBy('name')
|
||
->get()
|
||
->map(fn($e) => [
|
||
'id' => $e->id,
|
||
'name' => $e->name,
|
||
'nip' => $e->nip,
|
||
'position' => [
|
||
'id' => $e->position?->id,
|
||
'name' => $e->position?->name,
|
||
'basic_salary' => $e->position?->basic_salary ?? 0,
|
||
],
|
||
'department' => [
|
||
'name' => $e->department?->name,
|
||
],
|
||
]);
|
||
|
||
return Inertia::render('admin/payrolls/create', [
|
||
'employees' => $employees,
|
||
]);
|
||
}
|
||
|
||
public function store(Request $request)
|
||
{
|
||
$validated = $request->validate([
|
||
'employee_id' => 'required|exists:employees,id',
|
||
'period' => [
|
||
'required',
|
||
'date_format:Y-m',
|
||
Rule::unique('payrolls')->where(fn ($query) => $query->where('employee_id', $request->employee_id)),
|
||
],
|
||
'basic_salary' => 'required|integer|min:0',
|
||
'details' => 'nullable|array',
|
||
'details.*.name' => 'required|string|max:100',
|
||
'details.*.type' => 'required|in:bonus,deduction',
|
||
'details.*.amount' => 'required|integer|min:0',
|
||
], [
|
||
'period.date_format' => 'Format periode harus YYYY-MM.',
|
||
'period.unique' => 'Payroll karyawan untuk periode ini sudah ada.',
|
||
]);
|
||
|
||
// net_salary = basic_salary + Σbonus − Σpotongan, minimal 0
|
||
$net = $validated['basic_salary'];
|
||
foreach ($validated['details'] ?? [] as $item) {
|
||
$net += $item['type'] === 'bonus'
|
||
? $item['amount']
|
||
: -$item['amount'];
|
||
}
|
||
|
||
Payroll::create([
|
||
'employee_id' => $validated['employee_id'],
|
||
'period' => $validated['period'],
|
||
'basic_salary' => $validated['basic_salary'],
|
||
'details' => $validated['details'] ?? [],
|
||
'net_salary' => max(0, $net),
|
||
'status' => 'pending',
|
||
]);
|
||
|
||
return redirect('/admin/payrolls')
|
||
->with('success', 'Payroll berhasil di-generate.');
|
||
}
|
||
|
||
public function show(Payroll $payroll)
|
||
{
|
||
$payroll->load('employee.position', 'employee.department');
|
||
|
||
return Inertia::render('admin/payrolls/show', [
|
||
'payroll' => [
|
||
'id' => $payroll->id,
|
||
'period' => $payroll->period,
|
||
'basic_salary' => $payroll->basic_salary,
|
||
'details' => $payroll->details ?? [],
|
||
'net_salary' => $payroll->net_salary,
|
||
'status' => $payroll->status,
|
||
'created_at' => $payroll->created_at->format('d F Y'),
|
||
'employee' => [
|
||
'name' => $payroll->employee?->name,
|
||
'nip' => $payroll->employee?->nip,
|
||
'position' => $payroll->employee?->position?->name,
|
||
'department' => $payroll->employee?->department?->name,
|
||
],
|
||
],
|
||
]);
|
||
}
|
||
}
|