TIFNJK_E41222887/app/Http/Controllers/Admin/PayrollController.php

226 lines
8.7 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(Request $request)
{
$query = Payroll::with('employee');
if ($request->search) {
$query->whereHas('employee', function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%')
->orWhere('nip', 'like', '%' . $request->search . '%');
});
}
if ($request->period) {
$query->where('period', $request->period);
}
if ($request->status) {
$query->where('status', $request->status);
}
$payrolls = $query->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,
'filters' => $request->only(['search', 'period', 'status']),
]);
}
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',
], [
'employee_id.required' => 'Karyawan wajib dipilih.',
'employee_id.exists' => 'Karyawan tidak ditemukan.',
'period.required' => 'Periode gaji wajib diisi.',
'period.date_format' => 'Format periode harus YYYY-MM.',
'period.unique' => 'Payroll karyawan untuk periode ini sudah ada.',
'basic_salary.required' => 'Gaji pokok wajib diisi.',
'basic_salary.integer' => 'Gaji pokok harus berupa angka bulat.',
'basic_salary.min' => 'Gaji pokok tidak boleh negatif.',
'details.array' => 'Detail komponen harus berupa array.',
'details.*.name.required' => 'Nama komponen wajib diisi.',
'details.*.name.max' => 'Nama komponen maksimal 100 karakter.',
'details.*.type.required' => 'Tipe komponen wajib dipilih.',
'details.*.type.in' => 'Tipe komponen hanya boleh bonus atau potongan.',
'details.*.amount.required' => 'Nominal komponen wajib diisi.',
'details.*.amount.integer' => 'Nominal komponen harus berupa angka bulat.',
'details.*.amount.min' => 'Nominal komponen tidak boleh negatif.',
]);
$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,
],
],
]);
}
public function edit(Payroll $payroll)
{
$payroll->load('employee.position', 'employee.department');
return Inertia::render('admin/payrolls/edit', [
'payroll' => [
'id' => $payroll->id,
'period' => $payroll->period,
'basic_salary' => $payroll->basic_salary,
'details' => $payroll->details ?? [],
'net_salary' => $payroll->net_salary,
'status' => $payroll->status,
'employee' => [
'id' => $payroll->employee?->id,
'name' => $payroll->employee?->name,
'nip' => $payroll->employee?->nip,
'position' => $payroll->employee?->position?->name,
'department' => $payroll->employee?->department?->name,
],
],
]);
}
public function update(Request $request, Payroll $payroll)
{
$validated = $request->validate([
'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',
], [
'basic_salary.required' => 'Gaji pokok wajib diisi.',
'basic_salary.integer' => 'Gaji pokok harus berupa angka bulat.',
'basic_salary.min' => 'Gaji pokok tidak boleh negatif.',
'details.*.name.required' => 'Nama komponen wajib diisi.',
'details.*.type.required' => 'Tipe komponen wajib dipilih.',
'details.*.type.in' => 'Tipe hanya boleh bonus atau potongan.',
'details.*.amount.required' => 'Nominal wajib diisi.',
'details.*.amount.min' => 'Nominal tidak boleh negatif.',
]);
$net = $validated['basic_salary'];
foreach ($validated['details'] ?? [] as $item) {
$net += $item['type'] === 'bonus'
? $item['amount']
: -$item['amount'];
}
$payroll->update([
'basic_salary' => $validated['basic_salary'],
'details' => $validated['details'] ?? [],
'net_salary' => max(0, $net),
]);
return redirect()->route('admin.payrolls.show', $payroll)
->with('success', 'Data payroll berhasil diperbarui.');
}
/**
* Update hanya field status payroll (menunggu / telah_dikirim).
*/
public function updateStatus(Request $request, Payroll $payroll)
{
$validated = $request->validate([
'status' => ['required', Rule::in(['pending', 'paid'])],
], [
'status.required' => 'Status wajib dipilih.',
'status.in' => 'Status hanya boleh pending atau paid.',
]);
$payroll->update(['status' => $validated['status']]);
$label = $validated['status'] === 'paid' ? 'Telah Dikirim' : 'Menunggu';
return back()->with('success', "Status payroll diubah menjadi '{$label}'.");
}
}