hr dan payroll

This commit is contained in:
Wian Drs
2026-09-15 10:38:43 +07:00
parent 5e1fd11410
commit 8fedd39d8f
47 changed files with 1409 additions and 370 deletions
+13 -4
View File
@@ -8,6 +8,7 @@ $statusLabels=array('draft'=>'Draft','reviewed'=>'Sudah Direview','approved'=>'D
?>
<?php foreach(array('success','error')as$flash):if($this->session->flashdata($flash)):?><div data-app-toast data-icon="<?=$flash==='success'?'success':'error'?>" data-message="<?=html_escape($this->session->flashdata($flash))?>"></div><?php endif;endforeach?>
<?php if(!$can_manage_all_salary):?><div class="alert alert-warning"><i class="bi bi-lock-fill me-1"></i> Tampilan ini tidak memuat nilai maupun total gaji terbatas. Akses tersebut memerlukan permission <b>Manage All Gaji</b>.</div><?php endif?>
<?php if(!$can_manage_allowances&&in_array($payroll_section,array('salaries','settings','adjustments'),true)):?><div class="alert alert-info"><i class="bi bi-shield-lock-fill me-1"></i> Akses pengelolaan dibatasi pada <b>Gaji Pokok dan Potongan</b>. Tunjangan, bonus, lembur, THR, dan pendapatan tambahan memerlukan permission <b>Kelola Tunjangan &amp; Bonus</b>.</div><?php endif?>
<?php if($payroll_section==='periods'):?>
<div class="hr-workflow-strip" aria-label="Tahapan payroll"><span><b>1</b>Buat Periode</span><span><b>2</b>Lock Kehadiran</span><span><b>3</b>Hitung Draft</span><span><b>4</b>Review HR</span><span><b>5</b>Approve & Final</span><span><b>6</b>Posting Jurnal</span><span><b>7</b>Pembayaran</span></div>
@@ -32,23 +33,31 @@ $statusLabels=array('draft'=>'Draft','reviewed'=>'Sudah Direview','approved'=>'D
<ul class="nav nav-pills payroll-setting-tabs mb-3" role="tablist"><li class="nav-item"><button class="nav-link active" data-bs-toggle="pill" data-bs-target="#componentsTab">Master Komponen</button></li><li class="nav-item"><button class="nav-link" data-bs-toggle="pill" data-bs-target="#positionsTab">Template Posisi</button></li><li class="nav-item"><button class="nav-link" data-bs-toggle="pill" data-bs-target="#attendanceRulesTab">Aturan Kehadiran</button></li><li class="nav-item"><button class="nav-link" data-bs-toggle="pill" data-bs-target="#generalTab">Pengaturan Umum</button></li></ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="componentsTab"><section class="card hr-panel"><div class="card-header bg-white"><b>Master Komponen</b><small class="d-block text-muted">Komponen tidak otomatis berlaku untuk semua karyawan.</small></div><?php if($permissions['update']):?><div class="card-body border-bottom"><form method="post" action="<?=base_url('hrpayroll/component')?>" class="row g-2"><?=$csrf?><div class="col-md-2"><label class="form-label">Kode</label><input class="form-control" name="code" required></div><div class="col-md-3"><label class="form-label">Nama</label><input class="form-control" name="name" required></div><div class="col-md-2"><label class="form-label">Jenis</label><select class="form-select" name="component_type"><option value="earning">Pendapatan</option><option value="deduction">Potongan</option><option value="employer_cost">Beban perusahaan</option></select></div><div class="col-md-2"><label class="form-label">Kategori</label><select class="form-select" name="category"><option value="basic">Gaji Pokok</option><option value="allowance">Tunjangan</option><option value="overtime">Lembur</option><option value="unpaid_leave">Cuti tak dibayar</option><option value="loan">Pinjaman</option><option value="bpjs">BPJS</option><option value="tax">Pajak</option><option value="thr">THR</option><option value="bonus">Bonus</option><option value="other">Lainnya</option></select></div><div class="col-md-3"><label class="form-label">Metode</label><select class="form-select" name="calculation_method"><?php foreach($methodLabels as$key=>$label):?><option value="<?=$key?>"><?=html_escape($label)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nilai Default</label><input type="number" min="0" step="0.01" class="form-control" name="default_value" value="0"></div><div class="col-md-3"><label class="form-label">Formula</label><input class="form-control" name="formula_expression" placeholder="present_days * 25000"></div><div class="col-md-2"><label class="form-label">Akun Beban</label><select class="form-select searchable" name="expense_account_id"><option value="">Mapping umum</option><?php foreach($accounts as$a):?><option value="<?=$a->id?>"><?=html_escape($a->kode_akun.' - '.$a->nama_akun)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Akun Kewajiban</label><select class="form-select searchable" name="liability_account_id"><option value="">Mapping umum</option><?php foreach($accounts as$a):?><option value="<?=$a->id?>"><?=html_escape($a->kode_akun.' - '.$a->nama_akun)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Mulai Berlaku</label><input type="date" class="form-control" name="effective_from" value="<?=date('Y-m-d')?>"></div><div class="col-md-1 d-flex align-items-end"><label class="form-check mb-2"><input class="form-check-input" type="checkbox" name="is_active" value="1" checked> Aktif</label></div><div class="col-md-2 d-flex align-items-end justify-content-end"><button class="btn btn-primary">Simpan Komponen</button></div></form></div><?php endif?><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Kode</th><th>Komponen</th><th>Jenis</th><th>Metode</th><th class="text-end">Default</th><th>Mapping</th><th>Status</th></tr></thead><tbody><?php foreach($components as$c):?><tr><td><b><?=html_escape($c->code)?></b></td><td><?=html_escape($c->name)?></td><td><?=html_escape($c->component_type)?></td><td><?=html_escape($methodLabels[$c->calculation_method]??$c->calculation_method)?></td><td class="text-end">Rp <?=number_format($c->default_value??$c->fixed_amount,2,',','.')?></td><td><?=html_escape(($c->expense_account_id?:'mapping umum').' / '.($c->liability_account_id?:'mapping umum'))?></td><td><span class="badge <?=$c->is_active?'bg-success':'bg-secondary'?>"><?=$c->is_active?'Aktif':'Nonaktif'?></span></td></tr><?php endforeach?></tbody></table></div></section></div>
<div class="tab-pane fade" id="positionsTab"><section class="card hr-panel"><div class="card-header bg-white"><b>Template Posisi</b><small class="d-block text-muted">Override karyawan tetap memiliki prioritas di atas template posisi.</small></div><?php if($permissions['update']):?><div class="card-body border-bottom"><form method="post" action="<?=base_url('hrpayroll/save_position_line')?>" class="row g-2"><?=$csrf?><div class="col-md-3"><label class="form-label">Posisi</label><select class="form-select searchable" name="position_id" required><option value="">Pilih posisi</option><?php foreach($positions as$p):?><option value="<?=$p->id?>"><?=html_escape($p->position_name)?></option><?php endforeach?></select></div><div class="col-md-3"><label class="form-label">Komponen</label><select class="form-select searchable" name="component_id" required><option value="">Pilih komponen</option><?php foreach($components as$c):?><option value="<?=$c->id?>"><?=html_escape($c->code.' - '.$c->name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Metode</label><select class="form-select" name="calculation_method"><?php foreach($methodLabels as$key=>$label):?><option value="<?=$key?>"><?=html_escape($label)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nilai/Tarif</label><input type="number" min="0" step="0.01" class="form-control" name="amount_override" value="0"></div><div class="col-md-2"><label class="form-label">Mulai Berlaku</label><input type="date" class="form-control" name="effective_from" value="<?=date('Y-m-d')?>" required></div><div class="col-md-10"><input class="form-control" name="notes" placeholder="Keterangan template, opsional"></div><div class="col-md-2"><button class="btn btn-primary w-100">Tambah Template</button></div></form></div><?php endif?><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Posisi</th><th>Komponen</th><th>Metode</th><th class="text-end">Nilai</th><th>Efektif</th><th>Status</th></tr></thead><tbody><?php if(!$position_assignments):?><tr><td colspan="6" class="text-center text-muted py-4">Belum ada template posisi.</td></tr><?php endif;foreach($position_assignments as$x):?><tr><td><?=html_escape($x->position_name)?></td><td><?=html_escape($x->component_code.' - '.$x->component_name)?></td><td><?=html_escape($methodLabels[$x->calculation_method]??$x->calculation_method)?></td><td class="text-end">Rp <?=number_format($x->amount_override,2,',','.')?></td><td><?=html_escape($x->effective_from)?><?=!empty($x->effective_to)?' s.d. '.html_escape($x->effective_to):''?></td><td><span class="badge <?=$x->is_active?'bg-success':'bg-secondary'?>"><?=$x->is_active?'Aktif':'Nonaktif'?></span></td></tr><?php endforeach?></tbody></table></div></section></div>
<div class="tab-pane fade" id="positionsTab"><section class="card hr-panel"><div class="card-header bg-white"><b>Template Posisi</b><small class="d-block text-muted">Override karyawan tetap memiliki prioritas di atas template posisi.</small></div><?php if($permissions['update']):?><div class="card-body border-bottom"><form method="post" action="<?=base_url('hrpayroll/save_position_line')?>" class="row g-2"><?=$csrf?><div class="col-md-3"><label class="form-label">Posisi</label><select class="form-select searchable" name="position_id" required><option value="">Pilih posisi</option><?php foreach($positions as$p):?><option value="<?=$p->id?>"><?=html_escape($p->position_name)?></option><?php endforeach?></select></div><div class="col-md-3"><label class="form-label">Komponen</label><select class="form-select searchable" name="component_id" required><option value="">Pilih komponen</option><?php foreach($manageable_components as$c):?><option value="<?=$c->id?>"><?=html_escape($c->code.' - '.$c->name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Metode</label><select class="form-select" name="calculation_method"><?php foreach($methodLabels as$key=>$label):?><option value="<?=$key?>"><?=html_escape($label)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nilai/Tarif</label><input type="number" min="0" step="0.01" class="form-control" name="amount_override" value="0"></div><div class="col-md-2"><label class="form-label">Mulai Berlaku</label><input type="date" class="form-control" name="effective_from" value="<?=date('Y-m-d')?>" required></div><div class="col-md-10"><input class="form-control" name="notes" placeholder="Keterangan template, opsional"></div><div class="col-md-2"><button class="btn btn-primary w-100">Tambah Template</button></div></form></div><?php endif?><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Posisi</th><th>Komponen</th><th>Metode</th><th class="text-end">Nilai</th><th>Efektif</th><th>Status</th></tr></thead><tbody><?php if(!$position_assignments):?><tr><td colspan="6" class="text-center text-muted py-4">Belum ada template posisi.</td></tr><?php endif;foreach($position_assignments as$x):?><tr><td><?=html_escape($x->position_name)?></td><td><?=html_escape($x->component_code.' - '.$x->component_name)?></td><td><?=html_escape($methodLabels[$x->calculation_method]??$x->calculation_method)?></td><td class="text-end">Rp <?=number_format($x->amount_override,2,',','.')?></td><td><?=html_escape($x->effective_from)?><?=!empty($x->effective_to)?' s.d. '.html_escape($x->effective_to):''?></td><td><span class="badge <?=$x->is_active?'bg-success':'bg-secondary'?>"><?=$x->is_active?'Aktif':'Nonaktif'?></span></td></tr><?php endforeach?></tbody></table></div></section></div>
<div class="tab-pane fade" id="attendanceRulesTab"><section class="card hr-panel"><div class="card-header bg-white"><b>Aturan Kehadiran</b><small class="d-block text-muted">Terlambat secara default tidak dipotong. Cuti tahunan, sakit, izin, dan hari libur tidak dianggap alpa.</small></div><?php if($permissions['update']):?><div class="card-body border-bottom"><form method="post" action="<?=base_url('hrpayroll/save_attendance_rule')?>" class="row g-2"><?=$csrf?><div class="col-md-3"><label class="form-label">Nama Aturan</label><input class="form-control" name="rule_name" required></div><div class="col-md-2"><label class="form-label">Kondisi</label><select class="form-select" name="condition_type"><option value="alpha">Alpa</option><option value="late">Terlambat</option><option value="early_leave">Pulang cepat</option><option value="unpaid">Cuti tidak dibayar</option><option value="missing_checkin">Tidak check-in</option><option value="missing_checkout">Tidak check-out</option></select></div><div class="col-md-2"><label class="form-label">Metode Potongan</label><select class="form-select" name="calculation_method"><option value="none">Tidak dipotong</option><option value="fixed">Nominal tetap</option><option value="per_minute">Per menit</option><option value="per_hour">Per jam</option><option value="per_day">Per hari</option><option value="percentage_daily">Persentase tarif harian</option><option value="formula">Formula</option></select></div><div class="col-md-2"><label class="form-label">Komponen Potongan</label><select class="form-select searchable" name="component_id"><option value="">Tidak ada</option><?php foreach($components as$c):if($c->component_type!=='deduction')continue;?><option value="<?=$c->id?>"><?=html_escape($c->name)?></option><?php endforeach?></select></div><div class="col-md-1"><label class="form-label">Toleransi</label><input type="number" min="0" class="form-control" name="tolerance_minutes" value="0"></div><div class="col-md-2"><label class="form-label">Tarif</label><input type="number" min="0" step="0.01" class="form-control" name="rate_value" value="0"></div><div class="col-md-2"><label class="form-label">Maksimum</label><input type="number" min="0" step="0.01" class="form-control" name="maximum_amount"></div><div class="col-md-3"><label class="form-label">Mulai Berlaku</label><input type="date" class="form-control" name="effective_from" value="<?=date('Y-m-d')?>" required></div><div class="col-md-4"><label class="form-label">Formula / Keterangan</label><input class="form-control" name="formula_expression" placeholder="condition_quantity * rule_rate"></div><div class="col-md-1 d-flex align-items-end"><label class="form-check mb-2"><input class="form-check-input" type="checkbox" name="is_active" value="1" checked> Aktif</label></div><div class="col-md-2 d-flex align-items-end"><button class="btn btn-primary w-100">Simpan Versi</button></div></form></div><?php endif?><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Aturan</th><th>Kondisi</th><th>Metode</th><th>Toleransi</th><th class="text-end">Tarif</th><th>Komponen</th><th>Efektif</th></tr></thead><tbody><?php foreach($attendance_rules as$r):?><tr><td><?=html_escape($r->rule_name)?></td><td><?=html_escape($r->condition_type)?></td><td><?=html_escape($r->calculation_method)?></td><td><?=number_format($r->tolerance_minutes)?> menit</td><td class="text-end"><?=number_format($r->rate_value,2,',','.')?></td><td><?=html_escape($r->component_name?:'Tidak memotong')?></td><td><?=html_escape($r->effective_from)?></td></tr><?php endforeach?></tbody></table></div></section></div>
<div class="tab-pane fade" id="generalTab"><section class="card hr-panel"><div class="card-header bg-white"><b>Pengaturan Umum Payroll</b><small class="d-block text-muted">Berlaku untuk perusahaan aktif dan tidak mengubah histori payroll lama.</small></div><div class="card-body"><div class="row g-3 mb-4"><div class="col-md-4"><div class="payroll-info-box"><b>Sumber Kehadiran</b><span>Rentang tanggal pada periode payroll</span></div></div><div class="col-md-4"><div class="payroll-info-box"><b>Prioritas Komponen</b><span>Override karyawan → template posisi → nilai default</span></div></div><div class="col-md-4"><div class="payroll-info-box"><b>Pengamanan</b><span>Snapshot, separation of duties, idempotency jurnal</span></div></div></div><?php if($permissions['update']):?><form method="post" action="<?=base_url('hrpayroll/save_settings')?>" class="row g-3"><?=$csrf?><div class="col-md-4"><label class="form-label">Pemisahan Tugas</label><label class="form-check form-switch"><input class="form-check-input" type="checkbox" name="separation_of_duties" value="1" <?=!$payroll_settings||$payroll_settings->separation_of_duties?'checked':''?>> Reviewer, approver, dan finalizer harus berbeda</label></div><div class="col-md-3"><label class="form-label">Tanggal Bayar Default</label><input type="number" min="1" max="31" class="form-control" name="default_payment_day" value="<?=html_escape($payroll_settings->default_payment_day??'')?>" placeholder="1–31"></div><div class="col-md-5"><label class="form-label">Catatan Export Bank</label><div class="input-group"><input class="form-control" name="bank_export_note" value="<?=html_escape($payroll_settings->bank_export_note??'')?>" placeholder="Instruksi internal sebelum upload"><button class="btn btn-primary">Simpan</button></div></div></form><?php endif?><p class="text-muted small mt-3 mb-0">Akun jurnal utama dikelola melalui System Account Mapping. Akun khusus dapat dipilih pada masing-masing komponen.</p></div></section></div>
</div>
<?php else:?>
<?php if($permissions['create']):?><section class="card hr-panel mb-3"><div class="card-header bg-white"><b>Tambah Penyesuaian Satu Periode</b><small class="d-block text-muted">Tidak membuat jurnal saat disimpan dan tidak berulang ke periode berikutnya.</small></div><div class="card-body"><form method="post" action="<?=base_url('hrpayroll/save_adjustment')?>" class="row g-2"><?=$csrf?><div class="col-md-3"><label class="form-label">Periode Draft</label><select class="form-select searchable" name="payroll_period_id" required><option value="">Pilih periode</option><?php foreach($periods as$p):if($p->workflow_status!=='draft')continue;?><option value="<?=$p->id?>"><?=html_escape($p->period_name)?></option><?php endforeach?></select></div><div class="col-md-3"><label class="form-label">Karyawan</label><select class="form-select searchable" name="employee_id" required><option value="">Pilih karyawan</option><?php foreach($employees as$e):?><option value="<?=$e->id?>"><?=html_escape($e->employee_code.' - '.$e->full_name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Jenis</label><select class="form-select" name="adjustment_type"><option value="earning">Pendapatan</option><option value="deduction">Potongan</option></select></div><div class="col-md-2"><label class="form-label">Komponen</label><select class="form-select searchable" name="component_id" required><option value="">Pilih komponen</option><?php foreach($components as$c):if($c->component_type==='employer_cost')continue;?><option value="<?=$c->id?>"><?=html_escape($c->code.' - '.$c->name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nominal</label><input type="number" min="0.01" step="0.01" class="form-control" name="amount" required></div><div class="col-md-10"><label class="form-label">Keterangan</label><input class="form-control" name="notes" placeholder="Alasan bonus, rapel, koreksi, atau potongan" required></div><div class="col-md-2 d-flex align-items-end"><button class="btn btn-primary w-100">Simpan</button></div></form></div></section><?php endif?>
<section class="card hr-panel"><div class="card-header bg-white"><b>Daftar Penyesuaian</b></div><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Periode</th><th>Karyawan</th><th>Komponen</th><th>Jenis</th><th class="text-end">Nominal</th><th>Keterangan</th><th>Status</th><th>Aksi</th></tr></thead><tbody><?php if(!$adjustments):?><tr><td colspan="8" class="text-center text-muted py-5">Belum ada penyesuaian periode.</td></tr><?php endif;foreach($adjustments as$a):?><tr><td><?=html_escape($a->period_name)?></td><td><b><?=html_escape($a->full_name)?></b><small class="d-block text-muted"><?=html_escape($a->employee_code)?></small></td><td><?=html_escape($a->component_name)?></td><td><span class="badge <?=$a->adjustment_type==='earning'?'bg-success':'bg-warning text-dark'?>"><?=html_escape($a->adjustment_type==='earning'?'Pendapatan':'Potongan')?></span></td><td class="text-end">Rp <?=number_format($a->amount,2,',','.')?></td><td><?=html_escape($a->notes)?></td><td><?=html_escape($statusLabels[$a->workflow_status]??$a->workflow_status)?></td><td><?php if($permissions['update']&&$a->workflow_status==='draft'):?><form method="post" action="<?=base_url('hrpayroll/deactivate_adjustment/'.$a->id)?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-outline-danger">Nonaktifkan</button></form><?php else:?>-<?php endif?></td></tr><?php endforeach?></tbody></table></div></section>
<?php if($permissions['create']):?><section class="card hr-panel mb-3"><div class="card-header bg-white"><b>Tambah Penyesuaian Satu Periode</b><small class="d-block text-muted">Tidak membuat jurnal saat disimpan dan tidak berulang ke periode berikutnya.</small></div><div class="card-body"><form method="post" action="<?=base_url('hrpayroll/save_adjustment')?>" class="row g-2"><?=$csrf?><div class="col-md-3"><label class="form-label">Periode Draft</label><select class="form-select searchable" name="payroll_period_id" required><option value="">Pilih periode</option><?php foreach($periods as$p):if($p->workflow_status!=='draft')continue;?><option value="<?=$p->id?>"><?=html_escape($p->period_name)?></option><?php endforeach?></select></div><div class="col-md-3"><label class="form-label">Karyawan</label><select class="form-select searchable" name="employee_id" required><option value="">Pilih karyawan</option><?php foreach($employees as$e):?><option value="<?=$e->id?>"><?=html_escape($e->employee_code.' - '.$e->full_name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Jenis</label><select class="form-select" name="adjustment_type"><?php if($can_manage_allowances):?><option value="earning">Pendapatan / Bonus</option><?php endif?><option value="deduction">Potongan</option></select></div><div class="col-md-2"><label class="form-label">Komponen</label><select class="form-select searchable" name="component_id" required><option value="">Pilih komponen</option><?php foreach($adjustment_components as$c):?><option value="<?=$c->id?>" data-type="<?=html_escape($c->component_type)?>"><?=html_escape($c->code.' - '.$c->name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nominal</label><input type="number" min="0.01" step="0.01" class="form-control" name="amount" required></div><div class="col-md-10"><label class="form-label">Keterangan</label><input class="form-control" name="notes" placeholder="Alasan bonus, rapel, koreksi, atau potongan" required></div><div class="col-md-2 d-flex align-items-end"><button class="btn btn-primary w-100">Simpan</button></div></form></div></section><?php endif?>
<section class="card hr-panel"><div class="card-header bg-white"><b>Daftar Penyesuaian</b></div><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Periode</th><th>Karyawan</th><th>Komponen</th><th>Jenis</th><th class="text-end">Nominal</th><th>Keterangan</th><th>Status</th><th>Aksi</th></tr></thead><tbody><?php if(!$adjustments):?><tr><td colspan="8" class="text-center text-muted py-5">Belum ada penyesuaian periode.</td></tr><?php endif;foreach($adjustments as$a):?><tr><td><?=html_escape($a->period_name)?></td><td><b><?=html_escape($a->full_name)?></b><small class="d-block text-muted"><?=html_escape($a->employee_code)?></small></td><td><?=html_escape($a->component_name)?></td><td><span class="badge <?=$a->adjustment_type==='earning'?'bg-success':'bg-warning text-dark'?>"><?=html_escape($a->adjustment_type==='earning'?'Pendapatan':'Potongan')?></span></td><td class="text-end">Rp <?=number_format($a->amount,2,',','.')?></td><td><?=html_escape($a->notes)?></td><td><?=html_escape($statusLabels[$a->workflow_status]??$a->workflow_status)?></td><td><?php if($permissions['update']&&!empty($a->can_manage)&&$a->workflow_status==='draft'):?><form method="post" action="<?=base_url('hrpayroll/deactivate_adjustment/'.$a->id)?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-outline-danger">Nonaktifkan</button></form><?php else:?>-<?php endif?></td></tr><?php endforeach?></tbody></table></div></section>
<?php endif?>
<div class="modal fade" id="payrollDetailModal" tabindex="-1"><div class="modal-dialog modal-xl modal-dialog-scrollable"><div class="modal-content"><div class="modal-header"><div><small class="text-muted">RINCIAN PERIODE</small><h5 class="modal-title" id="payrollDetailTitle">Payroll</h5></div><button class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="table-responsive"><table class="table align-middle"><thead><tr><th>Karyawan</th><th class="text-end">Hadir</th><th class="text-end">Alpa</th><th class="text-end">Lembur</th><th class="text-end">Bruto</th><th class="text-end">Potongan</th><th class="text-end">Neto</th><th>Slip</th></tr></thead><tbody id="payrollDetailRows"></tbody></table></div></div></div></div></div>
<div class="modal fade" id="salaryModal" tabindex="-1" data-bs-backdrop="static"><div class="modal-dialog modal-xl modal-dialog-scrollable"><div class="modal-content"><div class="modal-header"><div><small class="text-muted">STRUKTUR GAJI EFEKTIF</small><h5 class="modal-title" id="salaryModalTitle">Karyawan</h5><small id="salaryModalMeta" class="text-muted"></small></div><button class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="table-responsive mb-4"><table class="table align-middle"><thead><tr><th>Komponen</th><th>Jenis</th><th>Metode</th><th class="text-end">Nilai/Tarif</th><th>Efektif</th><th>Status</th><th>Aksi</th></tr></thead><tbody id="salaryLineRows"></tbody></table></div><?php if($permissions['update']):?><form id="salaryLineForm" data-ajax="true" class="row g-2"><?=$csrf?><input type="hidden" name="employee_id" id="salaryEmployeeId"><div class="col-md-3"><label class="form-label">Komponen</label><select class="form-select" name="component_id" required><option value="">Pilih komponen</option><?php foreach($components as$c):?><option value="<?=$c->id?>"><?=html_escape($c->code.' - '.$c->name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Metode</label><select class="form-select" name="calculation_method"><?php foreach($methodLabels as$key=>$label):?><option value="<?=$key?>"><?=html_escape($label)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nilai/Tarif</label><input type="number" min="0" step="0.01" class="form-control" name="amount_override" value="0" required></div><div class="col-md-2"><label class="form-label">Mulai Berlaku</label><input type="date" class="form-control" name="effective_from" value="<?=date('Y-m-d')?>" required></div><div class="col-md-2"><label class="form-label">Selesai (opsional)</label><input type="date" class="form-control" name="effective_to"></div><div class="col-md-1 d-flex align-items-end"><button class="btn btn-primary w-100">Tambah</button></div><div class="col-md-6"><input class="form-control" name="formula_expression" placeholder="Formula bila metode Formula"></div><div class="col-md-6"><input class="form-control" name="notes" placeholder="Keterangan perubahan"></div></form><?php endif?></div></div></div></div>
<div class="modal fade" id="salaryModal" tabindex="-1" data-bs-backdrop="static"><div class="modal-dialog modal-xl modal-dialog-scrollable"><div class="modal-content"><div class="modal-header"><div><small class="text-muted">STRUKTUR GAJI EFEKTIF</small><h5 class="modal-title" id="salaryModalTitle">Karyawan</h5><small id="salaryModalMeta" class="text-muted"></small></div><button class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="table-responsive mb-4"><table class="table align-middle"><thead><tr><th>Komponen</th><th>Jenis</th><th>Metode</th><th class="text-end">Nilai/Tarif</th><th>Efektif</th><th>Status</th><th>Aksi</th></tr></thead><tbody id="salaryLineRows"></tbody></table></div><?php if($permissions['update']):?><form id="salaryLineForm" data-ajax="true" class="row g-2"><?=$csrf?><input type="hidden" name="employee_id" id="salaryEmployeeId"><div class="col-md-3"><label class="form-label">Komponen</label><select class="form-select" name="component_id" required><option value="">Pilih komponen</option><?php foreach($manageable_components as$c):?><option value="<?=$c->id?>"><?=html_escape($c->code.' - '.$c->name)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Metode</label><select class="form-select" name="calculation_method"><?php foreach($methodLabels as$key=>$label):?><option value="<?=$key?>"><?=html_escape($label)?></option><?php endforeach?></select></div><div class="col-md-2"><label class="form-label">Nilai/Tarif</label><input type="number" min="0" step="0.01" class="form-control" name="amount_override" value="0" required></div><div class="col-md-2"><label class="form-label">Mulai Berlaku</label><input type="date" class="form-control" name="effective_from" value="<?=date('Y-m-d')?>" required></div><div class="col-md-2"><label class="form-label">Selesai (opsional)</label><input type="date" class="form-control" name="effective_to"></div><div class="col-md-1 d-flex align-items-end"><button type="submit" class="btn btn-primary w-100">Tambah</button></div><div class="col-md-6"><input class="form-control" name="formula_expression" placeholder="Formula bila metode Formula"></div><div class="col-md-6"><input class="form-control" name="notes" placeholder="Keterangan perubahan"></div></form><?php endif?></div></div></div></div>
<script>
$(function(){
const esc=v=>$('<div>').text(v??'').html(),money=v=>'Rp '+Number(v||0).toLocaleString('id-ID',{minimumFractionDigits:2,maximumFractionDigits:2});
$('.searchable').select2({width:'100%'});
<?php if(!$can_manage_allowances):?>
$('#componentsTab select[name="component_type"] option').filter(function(){return this.value!=='deduction'}).remove();
$('#componentsTab select[name="category"] option').filter(function(){return !['unpaid_leave','loan','bpjs','tax','other'].includes(this.value)}).remove();
$('#generalTab form').remove();
<?php endif?>
const adjustmentForm=$('form[action$="hrpayroll/save_adjustment"]');
adjustmentForm.find('select[name="component_id"]').on('change',function(){const type=$(this).find(':selected').data('type');if(type)adjustmentForm.find('select[name="adjustment_type"]').val(type).trigger('change.select2')});
$(document).ajaxSuccess(function(_event,_xhr,settings,response){if(!String(settings.url||'').includes('hrpayroll/salary_detail/'))return;const lines=response?.data?.lines||response?.lines||[];$('#salaryLineRows tr').each(function(index){if(lines[index]&&!lines[index].can_manage)$(this).find('.deactivate-salary').remove()})});
if($('#salaryTable').length){const table=$('#salaryTable').DataTable({pageLength:25,order:[[0,'asc']],language:{search:'Cari:',lengthMenu:'Tampilkan _MENU_ data',info:'_START_–_END_ dari _TOTAL_',paginate:{previous:'Sebelumnya',next:'Berikutnya'}}});$('#salaryDepartmentFilter').on('change',function(){table.column(1).search(this.value).draw()});$('#salaryPositionFilter').on('change',function(){table.column(2).search(this.value).draw()});$('#salaryStatusFilter').on('change',function(){table.column(3).search(this.value).draw()})}
const detailModal=bootstrap.Modal.getOrCreateInstance('#payrollDetailModal');$('.payroll-detail').on('click',function(){$.getJSON('<?=base_url('hrpayroll/period_detail/')?>'+this.dataset.id).done(r=>{if(!r.status)return AppAlert.toast(r.message,'error');$('#payrollDetailTitle').text(r.period.period_name);$('#payrollDetailRows').html((r.rows||[]).map(x=>`<tr><td><b>${esc(x.full_name)}</b><small class="d-block text-muted">${esc(x.employee_code)}${x.salary_restricted==1?' · Gaji terbatas':''}</small></td><td class="text-end">${x.total_present}</td><td class="text-end">${x.total_alpha}</td><td class="text-end">${x.total_overtime_hours}</td><td class="text-end">${money(x.gross_salary)}</td><td class="text-end">${money(x.total_deductions)}</td><td class="text-end fw-bold">${money(x.total_salary)}</td><td>${['final','posted','paid'].includes(r.period.workflow_status)?`<a class="btn btn-sm btn-outline-primary" target="_blank" href="<?=base_url('hrpayroll/slip/')?>${x.id}">Slip</a>`:'-'}</td></tr>`).join('')||'<tr><td colspan="8" class="text-center text-muted">Belum ada rincian.</td></tr>');detailModal.show()}).fail(x=>AppAlert.toast(x.responseJSON?.message||'Rincian gagal dimuat.','error'))});
const salaryModal=bootstrap.Modal.getOrCreateInstance('#salaryModal');let currentEmployee=0;function loadSalary(id){currentEmployee=id;$('#salaryLineRows').html('<tr><td colspan="7" class="text-center py-4"><span class="spinner-border spinner-border-sm"></span> Memuat…</td></tr>');salaryModal.show();$.getJSON('<?=base_url('hrpayroll/salary_detail/')?>'+id).done(r=>{if(!r.status){salaryModal.hide();return AppAlert.toast(r.message,'error')}$('#salaryEmployeeId').val(id);$('#salaryModalTitle').text(r.data?.employee?.full_name||r.employee?.full_name||'Karyawan');const employee=r.data?.employee||r.employee,lines=r.data?.lines||r.lines||[];$('#salaryModalMeta').text((employee?.employee_code||'')+' · '+(employee?.department_name||'-')+' · '+(employee?.position_name||'-'));$('#salaryLineRows').html(lines.map(x=>`<tr><td><b>${esc(x.component_code)}</b><small class="d-block text-muted">${esc(x.component_name)}</small></td><td>${esc(x.component_type)}</td><td>${esc(x.calculation_method)}</td><td class="text-end">${money(x.amount_override)}</td><td>${esc(x.effective_from)}${x.effective_to?' s.d. '+esc(x.effective_to):''}</td><td><span class="badge ${x.is_active==1?'bg-success':'bg-secondary'}">${x.is_active==1?'Aktif':'Selesai'}</span></td><td>${x.is_active==1?`<button class="btn btn-sm btn-outline-danger deactivate-salary" data-id="${x.id}">Nonaktifkan</button>`:'-'}</td></tr>`).join('')||'<tr><td colspan="7" class="text-center text-muted py-4">Belum ada komponen gaji.</td></tr>')}).fail(x=>{salaryModal.hide();AppAlert.toast(x.responseJSON?.message||'Struktur gaji gagal dimuat.','error')})}