130 lines
4.2 KiB
PHP
130 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 Inertia\Inertia;
|
|
|
|
class PayrollController extends Controller
|
|
{
|
|
/**
|
|
* Tampilkan daftar semua payroll dengan Eager Loading (hindari N+1).
|
|
*/
|
|
public function index()
|
|
{
|
|
$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,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Tampilkan form Generate Payroll.
|
|
* Kirim daftar employees (beserta relasi position.basic_salary) ke frontend.
|
|
*/
|
|
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,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Simpan data payroll & hitung net_salary secara otomatis.
|
|
*
|
|
* Logika kalkulasi:
|
|
* net_salary = basic_salary
|
|
* + SUM(detail tipe "bonus")
|
|
* - SUM(detail tipe "deduction")
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'employee_id' => 'required|exists:employees,id',
|
|
'period' => 'required|string|max:7',
|
|
'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',
|
|
]);
|
|
|
|
$net = $validated['basic_salary'];
|
|
foreach ($validated['details'] ?? [] as $item) {
|
|
$net += $item['type'] === 'bonus'
|
|
? $item['amount']
|
|
: -$item['amount'];
|
|
}
|
|
$net = max(0, $net);
|
|
|
|
Payroll::create([
|
|
'employee_id' => $validated['employee_id'],
|
|
'period' => $validated['period'],
|
|
'basic_salary' => $validated['basic_salary'],
|
|
'details' => $validated['details'] ?? [],
|
|
'net_salary' => $net,
|
|
'status' => 'pending',
|
|
]);
|
|
|
|
return redirect('/admin/payrolls')
|
|
->with('success', 'Payroll berhasil di-generate.');
|
|
}
|
|
|
|
/**
|
|
* Tampilkan detail slip gaji 1 payroll.
|
|
*/
|
|
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,
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
}
|