refactor UI
This commit is contained in:
parent
cd50b5b10d
commit
859dbe13a1
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
class PayrollController extends Controller
|
||||
{
|
||||
/**
|
||||
* Tampilkan daftar semua payroll dengan Eager Loading (hindari N+1).
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Eager loading untuk mencegah N+1 query
|
||||
$payrolls = Payroll::with('employee')
|
||||
->latest()
|
||||
->get()
|
||||
|
|
@ -32,25 +30,21 @@ public function index()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->id,
|
||||
'name' => $e->name,
|
||||
'nip' => $e->nip,
|
||||
'position' => [
|
||||
'id' => $e->position?->id,
|
||||
'name' => $e->position?->name,
|
||||
'basic_salary' => $e->position?->basic_salary ?? 0,
|
||||
],
|
||||
'department' => [
|
||||
'department' => [
|
||||
'name' => $e->department?->name,
|
||||
],
|
||||
]);
|
||||
|
|
@ -60,14 +54,6 @@ public function create()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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([
|
||||
|
|
@ -80,20 +66,20 @@ public function store(Request $request)
|
|||
'details.*.amount' => 'required|integer|min:0',
|
||||
]);
|
||||
|
||||
// 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'];
|
||||
}
|
||||
$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,
|
||||
'net_salary' => max(0, $net),
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
|
|
@ -101,9 +87,6 @@ public function store(Request $request)
|
|||
->with('success', 'Payroll berhasil di-generate.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan detail slip gaji 1 payroll.
|
||||
*/
|
||||
public function show(Payroll $payroll)
|
||||
{
|
||||
$payroll->load('employee.position', 'employee.department');
|
||||
|
|
|
|||
|
|
@ -21,12 +21,9 @@ public function index()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim hanya karyawan yang BELUM memiliki akun user ke form create.
|
||||
* Query whereDoesntHave('user') mencegah 1 employee punya 2 akun.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
// whereDoesntHave mencegah 1 employee memiliki lebih dari 1 akun
|
||||
$employees = Employee::whereDoesntHave('user')
|
||||
->orderBy('name')
|
||||
->get()
|
||||
|
|
@ -42,19 +39,13 @@ public function create()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buat akun user baru yang terhubung ke Employee yang dipilih.
|
||||
* - Nama diambil otomatis dari Employee.
|
||||
* - Password di-set default 'password'.
|
||||
* - employee_id disimpan di kolom users.employee_id.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'employee_id' => [
|
||||
'required',
|
||||
'exists:employees,id',
|
||||
'unique:users,employee_id', // pastikan belum punya user
|
||||
'unique:users,employee_id',
|
||||
],
|
||||
'email' => 'required|email|max:255|unique:users,email',
|
||||
'role' => ['required', Rule::in(['admin', 'employee'])],
|
||||
|
|
@ -69,13 +60,12 @@ public function store(Request $request)
|
|||
'role.in' => 'Role hanya boleh admin atau employee.',
|
||||
]);
|
||||
|
||||
// Ambil nama dari profil Employee yang dipilih
|
||||
$employee = Employee::findOrFail($validated['employee_id']);
|
||||
|
||||
User::create([
|
||||
'name' => $employee->name,
|
||||
'email' => $validated['email'],
|
||||
'password' => Hash::make('password'), // default password
|
||||
'password' => Hash::make('password'),
|
||||
'role' => $validated['role'],
|
||||
'employee_id' => $employee->id,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ class Payroll extends Model
|
|||
'net_salary' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relasi ke Employee
|
||||
*/
|
||||
public function employee()
|
||||
{
|
||||
return $this->belongsTo(Employee::class);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
|
@ -10,14 +9,8 @@
|
|||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable, TwoFactorAuthenticatable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
|
|
@ -26,11 +19,6 @@ class User extends Authenticatable
|
|||
'employee_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'two_factor_secret',
|
||||
|
|
@ -43,7 +31,7 @@ public function employee()
|
|||
return $this->hasOne(Employee::class);
|
||||
}
|
||||
|
||||
/** Akses langsung ke profil Employee milik user ini */
|
||||
// employee_id di kolom users untuk relasi One-to-One langsung
|
||||
public function employeeProfile()
|
||||
{
|
||||
return $this->belongsTo(Employee::class, 'employee_id');
|
||||
|
|
@ -54,17 +42,12 @@ public function hasRole($role)
|
|||
return $this->role === $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'two_factor_confirmed_at' => 'datetime',
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'two_factor_confirmed_at'=> 'datetime',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ public function up(): void
|
|||
Schema::create('payrolls', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('employee_id')->constrained()->onDelete('cascade');
|
||||
$table->string('period'); // Format: "2026-04" (YYYY-MM)
|
||||
$table->string('period');
|
||||
$table->unsignedBigInteger('basic_salary');
|
||||
$table->json('details')->nullable(); // [{name, type, amount}]
|
||||
$table->json('details')->nullable();
|
||||
$table->unsignedBigInteger('net_salary');
|
||||
$table->enum('status', ['pending', 'paid'])->default('pending');
|
||||
$table->timestamps();
|
||||
|
|
|
|||
|
|
@ -6,18 +6,14 @@
|
|||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Tambah kolom employee_id (nullable) pada tabel users.
|
||||
* Nullable karena akun admin tidak harus punya profil Employee.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->foreignId('employee_id')
|
||||
->nullable()
|
||||
->unique() // One-to-One: 1 user = 1 employee
|
||||
->unique()
|
||||
->constrained('employees')
|
||||
->nullOnDelete() // Jika employee dihapus, kolom ini jadi NULL
|
||||
->nullOnDelete()
|
||||
->after('id');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ class DatabaseSeeder extends Seeder
|
|||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
UserSeeder::class, // Admin account (jangan dihapus, dibutuhkan login)
|
||||
MasterDataSeeder::class, // 1. Departments + Positions (dengan basic_salary)
|
||||
EmployeeSeeder::class, // 2. Users (employee) + profil Employee (FK ke dept & position)
|
||||
PayrollSeeder::class, // 3. Slip gaji (FK ke employee)
|
||||
UserSeeder::class,
|
||||
MasterDataSeeder::class,
|
||||
EmployeeSeeder::class,
|
||||
PayrollSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,266 +9,56 @@
|
|||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
/**
|
||||
* EmployeeSeeder
|
||||
*
|
||||
* Membuat akun User (role: employee) beserta profil Employee yang
|
||||
* terhubung ke Department dan Position yang sudah dibuat oleh MasterDataSeeder.
|
||||
*
|
||||
* Variasi kasus yang di-cover:
|
||||
* - Karyawan tetap (PKWTT) di berbagai departemen
|
||||
* - Karyawan kontrak (PKWT)
|
||||
* - Karyawan magang
|
||||
* - Berbagai jabatan dengan gaji pokok berbeda
|
||||
*/
|
||||
class EmployeeSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// ── Resolve Master Data by Name (aman terhadap perubahan ID) ──
|
||||
$depts = Department::pluck('id', 'name');
|
||||
$depts = Department::pluck('id', 'name');
|
||||
$positions = Position::pluck('id', 'name');
|
||||
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
// DATA KARYAWAN
|
||||
// Format: [user_data, employee_data]
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
$employees = [
|
||||
|
||||
// ── IT DEPARTMENT ────────────────────────────────────────
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Jono Joni',
|
||||
'email' => 'jono12@gmail.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2023001',
|
||||
'name' => 'Jono Joni',
|
||||
'gender' => 'L',
|
||||
'place_of_birth' => 'Jakarta',
|
||||
'birth_date' => '1992-05-14',
|
||||
'address' => 'Jl. Merdeka No.12, Jakarta Pusat',
|
||||
'phone_number' => '081234567890',
|
||||
'department_id' => $depts['Information Technology'],
|
||||
'position_id' => $positions['Senior Developer'],
|
||||
'status' => 'PKWTT',
|
||||
'join_date' => '2023-01-15',
|
||||
],
|
||||
'user' => ['name' => 'Jono Joni', 'email' => 'jono12@gmail.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2023001', 'name' => 'Jono Joni', 'gender' => 'L', 'place_of_birth' => 'Jakarta', 'birth_date' => '1992-05-14', 'address' => 'Jl. Merdeka No.12, Jakarta Pusat', 'phone_number' => '081234567890', 'department_id' => $depts['Information Technology'], 'position_id' => $positions['Senior Developer'], 'status' => 'PKWTT', 'join_date' => '2023-01-15'],
|
||||
],
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Budi Santoso',
|
||||
'email' => 'budi.santoso12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2023002',
|
||||
'name' => 'Budi Santoso',
|
||||
'gender' => 'L',
|
||||
'place_of_birth' => 'Bandung',
|
||||
'birth_date' => '1995-08-22',
|
||||
'address' => 'Jl. Sukajadi No.45, Bandung',
|
||||
'phone_number' => '082345678901',
|
||||
'department_id' => $depts['Information Technology'],
|
||||
'position_id' => $positions['Junior Developer'],
|
||||
'status' => 'PKWT',
|
||||
'join_date' => '2023-03-01',
|
||||
],
|
||||
'user' => ['name' => 'Budi Santoso', 'email' => 'budi.santoso12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2023002', 'name' => 'Budi Santoso', 'gender' => 'L', 'place_of_birth' => 'Bandung', 'birth_date' => '1995-08-22', 'address' => 'Jl. Sukajadi No.45, Bandung', 'phone_number' => '082345678901', 'department_id' => $depts['Information Technology'], 'position_id' => $positions['Junior Developer'], 'status' => 'PKWT', 'join_date' => '2023-03-01'],
|
||||
],
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Rizky Firmansyah',
|
||||
'email' => 'rizky.firmansyah12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2024001',
|
||||
'name' => 'Rizky Firmansyah',
|
||||
'gender' => 'L',
|
||||
'place_of_birth' => 'Surabaya',
|
||||
'birth_date' => '1998-11-30',
|
||||
'address' => 'Jl. Pemuda No.7, Surabaya',
|
||||
'phone_number' => '083456789012',
|
||||
'department_id' => $depts['Information Technology'],
|
||||
'position_id' => $positions['Junior Developer'],
|
||||
'status' => 'Magang',
|
||||
'join_date' => '2024-02-01',
|
||||
],
|
||||
'user' => ['name' => 'Rizky Firmansyah', 'email' => 'rizky.firmansyah12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2024001', 'name' => 'Rizky Firmansyah', 'gender' => 'L', 'place_of_birth' => 'Surabaya', 'birth_date' => '1998-11-30', 'address' => 'Jl. Pemuda No.7, Surabaya', 'phone_number' => '083456789012', 'department_id' => $depts['Information Technology'], 'position_id' => $positions['Junior Developer'], 'status' => 'Magang','join_date' => '2024-02-01'],
|
||||
],
|
||||
|
||||
// ── HR DEPARTMENT ────────────────────────────────────────
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Dewi Rahayu',
|
||||
'email' => 'dewi.rahayu12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2022001',
|
||||
'name' => 'Dewi Rahayu',
|
||||
'gender' => 'P',
|
||||
'place_of_birth' => 'Yogyakarta',
|
||||
'birth_date' => '1990-03-10',
|
||||
'address' => 'Jl. Malioboro No.88, Yogyakarta',
|
||||
'phone_number' => '084567890123',
|
||||
'department_id' => $depts['Human Resources'],
|
||||
'position_id' => $positions['Manager'],
|
||||
'status' => 'PKWTT',
|
||||
'join_date' => '2022-06-01',
|
||||
],
|
||||
'user' => ['name' => 'Dewi Rahayu', 'email' => 'dewi.rahayu12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2022001', 'name' => 'Dewi Rahayu', 'gender' => 'P', 'place_of_birth' => 'Yogyakarta', 'birth_date' => '1990-03-10', 'address' => 'Jl. Malioboro No.88, Yogyakarta', 'phone_number' => '084567890123', 'department_id' => $depts['Human Resources'], 'position_id' => $positions['Manager'], 'status' => 'PKWTT', 'join_date' => '2022-06-01'],
|
||||
],
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Anisa Putri',
|
||||
'email' => 'anisa.putri12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2023003',
|
||||
'name' => 'Anisa Putri',
|
||||
'gender' => 'P',
|
||||
'place_of_birth' => 'Semarang',
|
||||
'birth_date' => '1997-07-19',
|
||||
'address' => 'Jl. Pahlawan No.3, Semarang',
|
||||
'phone_number' => '085678901234',
|
||||
'department_id' => $depts['Human Resources'],
|
||||
'position_id' => $positions['HR Specialist'],
|
||||
'status' => 'PKWTT',
|
||||
'join_date' => '2023-07-01',
|
||||
],
|
||||
'user' => ['name' => 'Anisa Putri', 'email' => 'anisa.putri12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2023003', 'name' => 'Anisa Putri', 'gender' => 'P', 'place_of_birth' => 'Semarang', 'birth_date' => '1997-07-19', 'address' => 'Jl. Pahlawan No.3, Semarang', 'phone_number' => '085678901234', 'department_id' => $depts['Human Resources'], 'position_id' => $positions['HR Specialist'], 'status' => 'PKWTT', 'join_date' => '2023-07-01'],
|
||||
],
|
||||
|
||||
// ── FINANCE DEPARTMENT ───────────────────────────────────
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Hendra Kurniawan',
|
||||
'email' => 'hendra.kurniawan12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2021001',
|
||||
'name' => 'Hendra Kurniawan',
|
||||
'gender' => 'L',
|
||||
'place_of_birth' => 'Medan',
|
||||
'birth_date' => '1988-12-05',
|
||||
'address' => 'Jl. Sudirman No.22, Medan',
|
||||
'phone_number' => '086789012345',
|
||||
'department_id' => $depts['Finance'],
|
||||
'position_id' => $positions['Finance Analyst'],
|
||||
'status' => 'PKWTT',
|
||||
'join_date' => '2021-04-01',
|
||||
],
|
||||
'user' => ['name' => 'Hendra Kurniawan', 'email' => 'hendra.kurniawan12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2021001', 'name' => 'Hendra Kurniawan', 'gender' => 'L', 'place_of_birth' => 'Medan', 'birth_date' => '1988-12-05', 'address' => 'Jl. Sudirman No.22, Medan', 'phone_number' => '086789012345', 'department_id' => $depts['Finance'], 'position_id' => $positions['Finance Analyst'], 'status' => 'PKWTT', 'join_date' => '2021-04-01'],
|
||||
],
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Sari Wulandari',
|
||||
'email' => 'sari.wulandari12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2023004',
|
||||
'name' => 'Sari Wulandari',
|
||||
'gender' => 'P',
|
||||
'place_of_birth' => 'Solo',
|
||||
'birth_date' => '1996-02-28',
|
||||
'address' => 'Jl. Brigjen Katamso No.5, Solo',
|
||||
'phone_number' => '087890123456',
|
||||
'department_id' => $depts['Finance'],
|
||||
'position_id' => $positions['Staff Admin'],
|
||||
'status' => 'PKWT',
|
||||
'join_date' => '2023-09-01',
|
||||
],
|
||||
'user' => ['name' => 'Sari Wulandari', 'email' => 'sari.wulandari12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2023004', 'name' => 'Sari Wulandari', 'gender' => 'P', 'place_of_birth' => 'Solo', 'birth_date' => '1996-02-28', 'address' => 'Jl. Brigjen Katamso No.5, Solo', 'phone_number' => '087890123456', 'department_id' => $depts['Finance'], 'position_id' => $positions['Staff Admin'], 'status' => 'PKWT', 'join_date' => '2023-09-01'],
|
||||
],
|
||||
|
||||
// ── MARKETING DEPARTMENT ─────────────────────────────────
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Fajar Nugroho',
|
||||
'email' => 'fajar.nugroho12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2022002',
|
||||
'name' => 'Fajar Nugroho',
|
||||
'gender' => 'L',
|
||||
'place_of_birth' => 'Makassar',
|
||||
'birth_date' => '1993-09-15',
|
||||
'address' => 'Jl. Sultan Hasanuddin No.10, Makassar',
|
||||
'phone_number' => '088901234567',
|
||||
'department_id' => $depts['Marketing'],
|
||||
'position_id' => $positions['Marketing Staff'],
|
||||
'status' => 'PKWTT',
|
||||
'join_date' => '2022-11-01',
|
||||
],
|
||||
'user' => ['name' => 'Fajar Nugroho', 'email' => 'fajar.nugroho12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2022002', 'name' => 'Fajar Nugroho', 'gender' => 'L', 'place_of_birth' => 'Makassar', 'birth_date' => '1993-09-15', 'address' => 'Jl. Sultan Hasanuddin No.10, Makassar', 'phone_number' => '088901234567', 'department_id' => $depts['Marketing'], 'position_id' => $positions['Marketing Staff'], 'status' => 'PKWTT', 'join_date' => '2022-11-01'],
|
||||
],
|
||||
|
||||
// ── OPERATIONS DEPARTMENT ────────────────────────────────
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Linda Permatasari',
|
||||
'email' => 'linda.permata12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2024002',
|
||||
'name' => 'Linda Permatasari',
|
||||
'gender' => 'P',
|
||||
'place_of_birth' => 'Palembang',
|
||||
'birth_date' => '1999-04-20',
|
||||
'address' => 'Jl. Demang Lebar Daun No.8, Palembang',
|
||||
'phone_number' => '089012345678',
|
||||
'department_id' => $depts['Operations'],
|
||||
'position_id' => $positions['Operations Staff'],
|
||||
'status' => 'PKWT',
|
||||
'join_date' => '2024-01-10',
|
||||
],
|
||||
'user' => ['name' => 'Linda Permatasari','email' => 'linda.permata12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2024002', 'name' => 'Linda Permatasari','gender' => 'P', 'place_of_birth' => 'Palembang', 'birth_date' => '1999-04-20', 'address' => 'Jl. Demang Lebar Daun No.8, Palembang', 'phone_number' => '089012345678', 'department_id' => $depts['Operations'], 'position_id' => $positions['Operations Staff'], 'status' => 'PKWT', 'join_date' => '2024-01-10'],
|
||||
],
|
||||
|
||||
[
|
||||
'user' => [
|
||||
'name' => 'Agus Prasetyo',
|
||||
'email' => 'agus.prasetyo12@hris.com',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'employee',
|
||||
],
|
||||
'employee' => [
|
||||
'nip' => '2020001',
|
||||
'name' => 'Agus Prasetyo',
|
||||
'gender' => 'L',
|
||||
'place_of_birth' => 'Malang',
|
||||
'birth_date' => '1985-06-10',
|
||||
'address' => 'Jl. Ijen No.33, Malang',
|
||||
'phone_number' => '081112233445',
|
||||
'department_id' => $depts['Operations'],
|
||||
'position_id' => $positions['Manager'],
|
||||
'status' => 'PKWTT',
|
||||
'join_date' => '2020-08-01',
|
||||
],
|
||||
'user' => ['name' => 'Agus Prasetyo', 'email' => 'agus.prasetyo12@hris.com', 'password' => Hash::make('password'), 'role' => 'employee'],
|
||||
'employee' => ['nip' => '2020001', 'name' => 'Agus Prasetyo', 'gender' => 'L', 'place_of_birth' => 'Malang', 'birth_date' => '1985-06-10', 'address' => 'Jl. Ijen No.33, Malang', 'phone_number' => '081112233445', 'department_id' => $depts['Operations'], 'position_id' => $positions['Manager'], 'status' => 'PKWTT', 'join_date' => '2020-08-01'],
|
||||
],
|
||||
];
|
||||
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
// Buat User → lalu buat Employee yang terhubung ke user tersebut
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
foreach ($employees as $data) {
|
||||
$user = User::create($data['user']);
|
||||
Employee::create(array_merge($data['employee'], ['user_id' => $user->id]));
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@
|
|||
use App\Models\Position;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* MasterDataSeeder
|
||||
*
|
||||
* Seed departemen dan jabatan beserta gaji pokok masing-masing.
|
||||
* Seeder ini HARUS dijalankan sebelum EmployeeSeeder dan PayrollSeeder.
|
||||
*/
|
||||
class MasterDataSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
|
|
|
|||
|
|
@ -6,24 +6,11 @@
|
|||
use App\Models\Payroll;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* PayrollSeeder
|
||||
*
|
||||
* Membuat data payroll dummy yang realistis dan terhubung ke employee.
|
||||
* Seeder ini HARUS dijalankan setelah EmployeeSeeder.
|
||||
*
|
||||
* Variasi kasus yang di-cover:
|
||||
* - Status 'paid' (bulan lalu) dan 'pending' (bulan ini)
|
||||
* - Karyawan dengan bonus lembur, tunjangan jabatan, tunjangan transport
|
||||
* - Karyawan dengan potongan (BPJS, absen, pinjaman, keterlambatan)
|
||||
* - Karyawan magang (gaji pokok rendah, komponen minimal)
|
||||
* - Karyawan senior dengan tunjangan lengkap
|
||||
*/
|
||||
class PayrollSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// Helper: hitung net_salary otomatis (sama dengan logika Controller)
|
||||
// Kalkulasi net_salary konsisten dengan logika di PayrollController
|
||||
$net = function (int $basic, array $details): int {
|
||||
$total = $basic;
|
||||
foreach ($details as $item) {
|
||||
|
|
@ -32,177 +19,98 @@ public function run(): void
|
|||
return max(0, $total);
|
||||
};
|
||||
|
||||
// Resolve semua employee sekaligus (1 query, bukan N query)
|
||||
// Eager pluck: 1 query untuk semua employee
|
||||
$emp = Employee::pluck('id', 'name');
|
||||
|
||||
// Periode referensi
|
||||
$bulanLalu = now()->subMonth()->format('Y-m');
|
||||
$bulanIni = now()->format('Y-m');
|
||||
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
// DATASET PAYROLL
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
$records = [];
|
||||
|
||||
// ── 1. JONO JONI — Senior Developer (IT) ─────────────────────
|
||||
// Kasus: Karyawan senior, ada bonus proyek + tunjangan lengkap.
|
||||
// Bulan lalu: sudah dibayar. Bulan ini: masih pending.
|
||||
if (isset($emp['Jono Joni'])) {
|
||||
$basicJono = 9_000_000;
|
||||
|
||||
$basic = 9_000_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 600_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 450_000],
|
||||
['name' => 'Potongan BPJS Kesehatan','type' => 'deduction', 'amount' => 180_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 90_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Jono Joni'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicJono,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicJono, $detailLalu),
|
||||
'status' => 'paid',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 600_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 450_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 180_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 90_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Jono Joni'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
|
||||
$detailIni = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 600_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 450_000],
|
||||
['name' => 'Bonus Proyek Klien', 'type' => 'bonus', 'amount' => 1_500_000],
|
||||
['name' => 'Potongan BPJS Kesehatan','type' => 'deduction', 'amount' => 180_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 90_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Jono Joni'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicJono,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicJono, $detailIni),
|
||||
'status' => 'pending',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 600_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 450_000],
|
||||
['name' => 'Bonus Proyek Klien', 'type' => 'bonus', 'amount' => 1_500_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 180_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 90_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Jono Joni'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 2. BUDI SANTOSO — Junior Developer (IT) ──────────────────
|
||||
// Kasus: Karyawan kontrak, ada potongan keterlambatan.
|
||||
if (isset($emp['Budi Santoso'])) {
|
||||
$basicBudi = 5_500_000;
|
||||
|
||||
$basic = 5_500_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 400_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 55_000],
|
||||
['name' => 'Potongan Keterlambatan', 'type' => 'deduction', 'amount' => 100_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Budi Santoso'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicBudi,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicBudi, $detailLalu),
|
||||
'status' => 'paid',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 400_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 55_000],
|
||||
['name' => 'Potongan Keterlambatan', 'type' => 'deduction', 'amount' => 100_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Budi Santoso'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
|
||||
$detailIni = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 400_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 55_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Budi Santoso'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicBudi,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicBudi, $detailIni),
|
||||
'status' => 'pending',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 400_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 55_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Budi Santoso'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 3. RIZKY FIRMANSYAH — Junior Developer Magang (IT) ───────
|
||||
// Kasus: Karyawan magang, komponen sangat minimal, tidak ada BPJS penuh.
|
||||
if (isset($emp['Rizky Firmansyah'])) {
|
||||
$basicRizky = 5_500_000; // gaji pokok jabatan "Junior Developer"
|
||||
|
||||
$basic = 5_500_000;
|
||||
$detailIni = [
|
||||
['name' => 'Uang Transport Magang', 'type' => 'bonus', 'amount' => 200_000],
|
||||
['name' => 'Potongan Tidak Hadir', 'type' => 'deduction', 'amount' => 250_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Rizky Firmansyah'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicRizky,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicRizky, $detailIni),
|
||||
'status' => 'pending',
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Rizky Firmansyah'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 4. DEWI RAHAYU — Manager HR ──────────────────────────────
|
||||
// Kasus: Manajer dengan tunjangan jabatan besar + BPJS penuh.
|
||||
if (isset($emp['Dewi Rahayu'])) {
|
||||
$basicDewi = 12_000_000;
|
||||
|
||||
$basic = 12_000_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Jabatan', 'type' => 'bonus', 'amount' => 2_000_000],
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 700_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 240_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 120_000],
|
||||
['name' => 'Potongan PPh 21', 'type' => 'deduction', 'amount' => 500_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Dewi Rahayu'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicDewi,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicDewi, $detailLalu),
|
||||
'status' => 'paid',
|
||||
['name' => 'Tunjangan Jabatan', 'type' => 'bonus', 'amount' => 2_000_000],
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 700_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 240_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 120_000],
|
||||
['name' => 'Potongan PPh 21', 'type' => 'deduction', 'amount' => 500_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Dewi Rahayu'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
|
||||
$detailIni = [
|
||||
['name' => 'Tunjangan Jabatan', 'type' => 'bonus', 'amount' => 2_000_000],
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 700_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Bonus Kinerja Triwulan', 'type' => 'bonus', 'amount' => 1_000_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 240_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 120_000],
|
||||
['name' => 'Potongan PPh 21', 'type' => 'deduction', 'amount' => 600_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Dewi Rahayu'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicDewi,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicDewi, $detailIni),
|
||||
'status' => 'pending',
|
||||
['name' => 'Tunjangan Jabatan', 'type' => 'bonus', 'amount' => 2_000_000],
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 700_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Bonus Kinerja Triwulan', 'type' => 'bonus', 'amount' => 1_000_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 240_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 120_000],
|
||||
['name' => 'Potongan PPh 21', 'type' => 'deduction', 'amount' => 600_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Dewi Rahayu'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 5. ANISA PUTRI — HR Specialist ───────────────────────────
|
||||
// Kasus: Karyawan tetap biasa, komponen standard.
|
||||
if (isset($emp['Anisa Putri'])) {
|
||||
$basicAnisa = 5_800_000;
|
||||
|
||||
$basic = 5_800_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 400_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 116_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 58_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Anisa Putri'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicAnisa,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicAnisa, $detailLalu),
|
||||
'status' => 'paid',
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Anisa Putri'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
}
|
||||
|
||||
// ── 6. HENDRA KURNIAWAN — Finance Analyst ────────────────────
|
||||
// Kasus: Karyawan lama, ada potongan cicilan pinjaman perusahaan.
|
||||
if (isset($emp['Hendra Kurniawan'])) {
|
||||
$basicHendra = 7_000_000;
|
||||
|
||||
$basic = 7_000_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 450_000],
|
||||
|
|
@ -210,111 +118,51 @@ public function run(): void
|
|||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 70_000],
|
||||
['name' => 'Cicilan Pinjaman', 'type' => 'deduction', 'amount' => 500_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Hendra Kurniawan'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicHendra,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicHendra, $detailLalu),
|
||||
'status' => 'paid',
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Hendra Kurniawan'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
|
||||
$detailIni = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 450_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 140_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 70_000],
|
||||
['name' => 'Cicilan Pinjaman', 'type' => 'deduction', 'amount' => 500_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Hendra Kurniawan'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicHendra,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicHendra, $detailIni),
|
||||
'status' => 'pending',
|
||||
];
|
||||
$detailIni = $detailLalu;
|
||||
$records[] = ['employee_id' => $emp['Hendra Kurniawan'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 7. SARI WULANDARI — Staff Admin Finance (PKWT) ───────────
|
||||
// Kasus: Kontrak, ada potongan absen, tidak ada bonus.
|
||||
if (isset($emp['Sari Wulandari'])) {
|
||||
$basicSari = 4_000_000;
|
||||
|
||||
$basic = 4_000_000;
|
||||
$detailIni = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 300_000],
|
||||
['name' => 'Potongan Absen', 'type' => 'deduction', 'amount' => 200_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 40_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Sari Wulandari'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicSari,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicSari, $detailIni),
|
||||
'status' => 'pending',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 300_000],
|
||||
['name' => 'Potongan Absen', 'type' => 'deduction', 'amount' => 200_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 40_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Sari Wulandari'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 8. FAJAR NUGROHO — Marketing Staff ───────────────────────
|
||||
// Kasus: Marketing, ada bonus komisi penjualan bulan lalu.
|
||||
if (isset($emp['Fajar Nugroho'])) {
|
||||
$basicFajar = 4_500_000;
|
||||
|
||||
$basic = 4_500_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Komisi Penjualan', 'type' => 'bonus', 'amount' => 2_000_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 45_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Fajar Nugroho'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicFajar,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicFajar, $detailLalu),
|
||||
'status' => 'paid',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Komisi Penjualan', 'type' => 'bonus', 'amount' => 2_000_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 45_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Fajar Nugroho'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
|
||||
$detailIni = [
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 45_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Fajar Nugroho'],
|
||||
'period' => $bulanIni,
|
||||
'basic_salary' => $basicFajar,
|
||||
'details' => $detailIni,
|
||||
'net_salary' => $net($basicFajar, $detailIni),
|
||||
'status' => 'pending',
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 350_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 45_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Fajar Nugroho'], 'period' => $bulanIni, 'basic_salary' => $basic, 'details' => $detailIni, 'net_salary' => $net($basic, $detailIni), 'status' => 'pending'];
|
||||
}
|
||||
|
||||
// ── 9. AGUS PRASETYO — Manager Operations ────────────────────
|
||||
// Kasus: Manager senior dengan PPh 21 dan semua tunjangan.
|
||||
if (isset($emp['Agus Prasetyo'])) {
|
||||
$basicAgus = 12_000_000;
|
||||
|
||||
$basic = 12_000_000;
|
||||
$detailLalu = [
|
||||
['name' => 'Tunjangan Jabatan', 'type' => 'bonus', 'amount' => 2_500_000],
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 700_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 240_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 120_000],
|
||||
['name' => 'Potongan PPh 21', 'type' => 'deduction', 'amount' => 650_000],
|
||||
];
|
||||
$records[] = [
|
||||
'employee_id' => $emp['Agus Prasetyo'],
|
||||
'period' => $bulanLalu,
|
||||
'basic_salary' => $basicAgus,
|
||||
'details' => $detailLalu,
|
||||
'net_salary' => $net($basicAgus, $detailLalu),
|
||||
'status' => 'paid',
|
||||
['name' => 'Tunjangan Jabatan', 'type' => 'bonus', 'amount' => 2_500_000],
|
||||
['name' => 'Tunjangan Transport', 'type' => 'bonus', 'amount' => 700_000],
|
||||
['name' => 'Tunjangan Makan', 'type' => 'bonus', 'amount' => 500_000],
|
||||
['name' => 'Potongan BPJS Kesehatan', 'type' => 'deduction', 'amount' => 240_000],
|
||||
['name' => 'Potongan BPJS TK', 'type' => 'deduction', 'amount' => 120_000],
|
||||
['name' => 'Potongan PPh 21', 'type' => 'deduction', 'amount' => 650_000],
|
||||
];
|
||||
$records[] = ['employee_id' => $emp['Agus Prasetyo'], 'period' => $bulanLalu, 'basic_salary' => $basic, 'details' => $detailLalu, 'net_salary' => $net($basic, $detailLalu), 'status' => 'paid'];
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
// Insert semua record
|
||||
// ──────────────────────────────────────────────────────────────
|
||||
foreach ($records as $data) {
|
||||
Payroll::create($data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import {
|
|||
import AppLayout from '@/layouts/app-layout';
|
||||
import { PlusCircle, FileText } from 'lucide-react';
|
||||
|
||||
// ─── Types ───────────────────────────────────────────────────────────────────
|
||||
|
||||
interface Payroll {
|
||||
id: number;
|
||||
employee_name: string;
|
||||
|
|
@ -28,8 +26,6 @@ interface PageProps {
|
|||
payrolls: Payroll[];
|
||||
}
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function formatRupiah(value: number): string {
|
||||
return new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
|
|
@ -38,14 +34,19 @@ function formatRupiah(value: number): string {
|
|||
}).format(value);
|
||||
}
|
||||
|
||||
/** Format "2026-04" → "April 2026" */
|
||||
function formatPeriod(period: string): string {
|
||||
const [year, month] = period.split('-');
|
||||
const date = new Date(Number(year), Number(month) - 1, 1);
|
||||
return date.toLocaleDateString('id-ID', { month: 'long', year: 'numeric' });
|
||||
return new Date(Number(year), Number(month) - 1, 1)
|
||||
.toLocaleDateString('id-ID', { month: 'long', year: 'numeric' });
|
||||
}
|
||||
|
||||
// ─── Component ───────────────────────────────────────────────────────────────
|
||||
function StatusBadge({ status }: { status: 'pending' | 'paid' }) {
|
||||
return status === 'paid' ? (
|
||||
<Badge className="bg-emerald-100 text-emerald-700 hover:bg-emerald-100">Paid</Badge>
|
||||
) : (
|
||||
<Badge className="bg-amber-100 text-amber-700 hover:bg-amber-100">Pending</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Index({ payrolls }: PageProps) {
|
||||
return (
|
||||
|
|
@ -53,8 +54,6 @@ export default function Index({ payrolls }: PageProps) {
|
|||
<Head title="Daftar Payroll" />
|
||||
|
||||
<div className="p-4 md:p-8 w-full space-y-4">
|
||||
|
||||
{/* Page Header */}
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-2xl font-bold tracking-tight">Manajemen Payroll</h2>
|
||||
<p className="text-muted-foreground">Daftar seluruh payroll yang telah di-generate.</p>
|
||||
|
|
@ -131,20 +130,3 @@ export default function Index({ payrolls }: PageProps) {
|
|||
</AppLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Sub-component: Status Badge ─────────────────────────────────────────────
|
||||
|
||||
function StatusBadge({ status }: { status: 'pending' | 'paid' }) {
|
||||
if (status === 'paid') {
|
||||
return (
|
||||
<Badge className="bg-emerald-100 text-emerald-700 hover:bg-emerald-100">
|
||||
Paid
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Badge className="bg-amber-100 text-amber-700 hover:bg-amber-100">
|
||||
Pending
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue