update HR dan Payroll

This commit is contained in:
Wian Drs
2026-09-14 11:14:25 +07:00
parent ec94783db4
commit 63993ee76e
50 changed files with 2432 additions and 1473 deletions
+39
View File
@@ -0,0 +1,39 @@
<?php
$hrActive = isset($active_menu) ? $active_menu : '';
$hrTitle = isset($title) ? $title : 'HR & Payroll';
$hrSubtitle = isset($subtitle) ? $subtitle : 'Kelola administrasi karyawan secara tertib dan mudah dipahami.';
$employeePages = array('employees','organization_master');
$attendancePages = array('generate_schedule','attendance_monitoring','attendance_monthly','shift','holidays','leave_requests');
if (in_array($hrActive, $employeePages, true)) {
$hrLinks = array(
array('employees','Data Karyawan','employees','hr_employees','bi-person-vcard'),
array('organization_master','Departemen & Posisi','organizationmaster','hr_organization','bi-diagram-3')
);
} elseif (in_array($hrActive, $attendancePages, true)) {
$hrLinks = array(
array('generate_schedule','Jadwal Kerja','generateschedule','hr_schedules','bi-calendar3'),
array('attendance_monitoring','Monitoring Absen','attendancemonitoring','hr_attendance','bi-person-check'),
array('attendance_monthly','Absen Bulanan','attendancemonthly','hr_attendance','bi-calendar-check'),
array('holidays','Hari Libur','holidays','hr_schedules','bi-calendar-event'),
array('shift','Shift','shifts','hr_schedules','bi-clock'),
array('leave_requests','Cuti & Izin','leaverequests','hr_leave','bi-calendar2-minus')
);
} else {
$hrLinks = array(
array('hr_payroll','Daftar Periode Gaji','hrpayroll?section=periods','payroll','bi-calendar2-range'),
array('hr_payroll_salary','Master Gaji Karyawan','hrpayroll?section=salaries','payroll','bi-person-lines-fill'),
array('hr_payroll_settings','Setting Payroll','hrpayroll?section=settings','payroll','bi-sliders'),
array('hr_payroll_adjustments','Penyesuaian Periode','hrpayroll?section=adjustments','payroll','bi-plus-minus')
);
}
?>
<section class="hr-module-hero">
<div><span>HR & PAYROLL WORKSPACE</span><h2 class="text-light"><?=html_escape($hrTitle)?></h2><p><?=html_escape($hrSubtitle)?></p></div>
<?php if (!empty($action_html)) echo $action_html; ?>
</section>
<nav class="hr-module-nav" aria-label="Navigasi HR & Payroll">
<?php foreach ($hrLinks as $link): if (!is_master_admin_user() && !check_permission($link[3],'can_view') && !check_permission('employees','can_view')) continue; ?>
<?php $isActive = $hrActive === $link[0] || ($hrActive === 'hr_payroll' && strpos($link[2], 'section='.((string)($payroll_section ?? 'periods'))) !== false); ?>
<a class="<?=$isActive?'active':''?>" href="<?=base_url($link[2])?>"><i class="bi <?=$link[4]?>"></i><span><?=html_escape($link[1])?></span></a>
<?php endforeach; ?>
</nav>
+57 -3
View File
@@ -1,3 +1,57 @@
<div class="container-fluid py-3"><h4>HR & Payroll Control</h4><p class="text-muted">Lock absensi → Draft → Review HR → Approval → Final → Jurnal → Pembayaran.</p><?php foreach(array('success','error')as$f)if($this->session->flashdata($f)):?><div class="alert alert-<?=$f==='success'?'success':'danger'?>"><?=html_escape($this->session->flashdata($f))?></div><?php endif?><div class="row g-3"><div class="col-lg-4"><div class="card h-100"><div class="card-header">1. Lock Absensi Bulanan</div><div class="card-body"><form method="post" action="<?=base_url('hrpayroll/lock_attendance')?>"><input class="form-control mb-2" type="month" name="period" value="<?=date('Y-m')?>" required><input class="form-control mb-2" name="notes" required placeholder="Catatan validasi absensi"><button class="btn btn-warning">Validasi & Lock</button></form></div></div></div><div class="col-lg-4"><div class="card h-100"><div class="card-header">2. Hitung Draft Payroll</div><div class="card-body"><form method="post" action="<?=base_url('hrpayroll/calculate')?>"><input class="form-control mb-2" type="month" name="month" value="<?=date('Y-m')?>" required><p class="small">Menggunakan snapshot absensi terkunci dan formula komponen aktif.</p><button class="btn btn-primary">Generate/Hitung Ulang Draft</button></form></div></div></div><div class="col-lg-4"><div class="card h-100"><div class="card-header">Formula Configurable</div><div class="card-body"><small>Variabel: basic_salary, work_days, present_days, unpaid_days, overtime_hours, overtime_rate, daily_rate.</small><form class="mt-2" method="post" action="<?=base_url('hrpayroll/component')?>"><div class="d-flex gap-1"><input class="form-control" name="code" placeholder="Kode" required><input class="form-control" name="name" placeholder="Nama" required></div><div class="d-flex gap-1 my-1"><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><select class="form-select" name="category"><option>allowance</option><option>overtime</option><option>unpaid_leave</option><option>loan</option><option>bpjs</option><option>tax</option><option>thr</option><option>bonus</option><option>other</option></select></div><input class="form-control mb-1" name="formula_expression" placeholder="Contoh: overtime_hours * overtime_rate"><input class="form-control mb-1" type="number" name="fixed_amount" placeholder="Atau nominal tetap"><label class="me-2"><input type="checkbox" name="taxable" value="1"> Taxable</label><label><input type="checkbox" name="prorated" value="1"> Prorata</label><br><button class="btn btn-sm btn-outline-primary mt-2">Simpan Komponen</button></form></div></div></div></div>
<div class="card mt-3"><div class="card-header">Payroll Register</div><div class="table-responsive"><table class="table mb-0"><tr><th>Periode</th><th>Karyawan</th><th>Bruto</th><th>Potongan</th><th>Neto</th><th>Status</th><th>Workflow</th></tr><?php foreach($periods as$p):?><tr><td><?=html_escape($p->period_name)?></td><td><?=$p->employee_count?></td><td class="text-end"><?=number_format($p->gross,0,',','.')?></td><td class="text-end"><?=number_format($p->deductions,0,',','.')?></td><td class="text-end"><?=number_format($p->net,0,',','.')?></td><td><span class="badge bg-secondary"><?=$p->workflow_status?></span></td><td><div class="d-flex gap-1"><?php if($p->workflow_status==='draft'):?><form method="post" action="<?=base_url('hrpayroll/workflow/'.$p->id.'/review')?>"><button class="btn btn-sm btn-info">Review HR</button></form><?php elseif($p->workflow_status==='reviewed'):?><form method="post" action="<?=base_url('hrpayroll/workflow/'.$p->id.'/approve')?>"><button class="btn btn-sm btn-warning">Approve</button></form><?php elseif($p->workflow_status==='approved'):?><form method="post" action="<?=base_url('hrpayroll/workflow/'.$p->id.'/finalize')?>"><button class="btn btn-sm btn-danger">Final & Lock</button></form><?php elseif($p->workflow_status==='final'):?><form class="d-flex" method="post" action="<?=base_url('hrpayroll/post/'.$p->id)?>"><input class="form-control form-control-sm" type="date" name="date" value="<?=$p->end_date?>"><button class="btn btn-sm btn-success">Post Jurnal</button></form><?php elseif($p->workflow_status==='posted'):?><form class="d-flex" method="post" action="<?=base_url('hrpayroll/pay/'.$p->id)?>"><input class="form-control form-control-sm" type="date" name="date" value="<?=date('Y-m-d')?>"><select class="form-select form-select-sm" name="cash_account_id"><?php foreach($cash_accounts as$c):?><option value="<?=$c->id?>"><?=html_escape($c->name)?></option><?php endforeach?></select><button class="btn btn-sm btn-primary">Bayar</button></form><?php endif?></div></td></tr><?php endforeach?></table></div></div>
<div class="card mt-3"><div class="card-header">Komponen Aktif</div><div class="table-responsive"><table class="table table-sm"><tr><th>Kode</th><th>Nama</th><th>Jenis</th><th>Kategori</th><th>Formula/Nominal</th><th>Pajak</th></tr><?php foreach($components as$c):?><tr><td><?=$c->code?></td><td><?=html_escape($c->name)?></td><td><?=$c->component_type?></td><td><?=$c->category?></td><td><?=html_escape($c->formula_expression?:number_format($c->fixed_amount,0,',','.'))?></td><td><?=$c->taxable?'Ya':'Tidak'?></td></tr><?php endforeach?></table></div></div></div>
<?php
$sectionLabels=array('periods'=>array('hr_payroll','Daftar Periode Gaji','Kelola rentang kehadiran, proses payroll, jurnal, dan pembayaran dalam satu alur.'),'salaries'=>array('hr_payroll_salary','Master Gaji Karyawan','Atur pendapatan dan potongan berulang dengan histori tanggal efektif.'),'settings'=>array('hr_payroll_settings','Setting Payroll','Kelola komponen, template posisi, dan aturan kehadiran tanpa input berulang setiap bulan.'),'adjustments'=>array('hr_payroll_adjustments','Penyesuaian Periode','Catat bonus, rapel, insentif, atau potongan yang hanya berlaku pada satu periode.'));
$meta=$sectionLabels[$payroll_section];
$this->load->view('hr_payroll/_header',array('active_menu'=>$meta[0],'payroll_section'=>$payroll_section,'title'=>$meta[1],'subtitle'=>$meta[2]));
$csrf='<input type="hidden" name="'.$this->security->get_csrf_token_name().'" value="'.$this->security->get_csrf_hash().'">';
$methodLabels=array('fixed'=>'Nominal tetap','per_present_day'=>'Per hari hadir','per_absent_day'=>'Per hari tidak hadir','per_minute'=>'Per menit','per_hour'=>'Per jam','percentage_basic'=>'Persentase gaji pokok','formula'=>'Formula tervalidasi');
$statusLabels=array('draft'=>'Draft','reviewed'=>'Sudah Direview','approved'=>'Disetujui','final'=>'Final','posted'=>'Terposting','paid'=>'Dibayar','cancelled'=>'Dibatalkan');
?>
<?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($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>
<?php if($permissions['create']):?><section class="card hr-panel mb-3"><div class="card-header bg-white"><b>Buat Periode Gaji</b><small class="d-block text-muted">Rentang kehadiran bebas melintasi bulan; periode pada perusahaan yang sama tidak boleh tumpang tindih.</small></div><div class="card-body"><form method="post" action="<?=base_url('hrpayroll/create_period')?>" class="row g-3"><?=$csrf?><div class="col-lg-3"><label class="form-label">Nama Periode</label><input class="form-control" name="period_name" placeholder="Contoh: Gaji September 2026" required></div><div class="col-md-3 col-lg-2"><label class="form-label">Awal Kehadiran</label><input type="date" class="form-control" name="attendance_start_date" required></div><div class="col-md-3 col-lg-2"><label class="form-label">Akhir Kehadiran</label><input type="date" class="form-control" name="attendance_end_date" required></div><div class="col-md-3 col-lg-2"><label class="form-label">Tanggal Bayar</label><input type="date" class="form-control" name="payment_date" required></div><div class="col-lg-3"><label class="form-label">Keterangan</label><div class="input-group"><input class="form-control" name="notes" placeholder="Opsional"><button class="btn btn-primary"><i class="bi bi-plus-lg"></i> Buat</button></div></div></form></div></section><?php endif?>
<section class="card hr-panel"><div class="card-header bg-white"><b>Daftar Periode Payroll</b><small class="d-block text-muted">Aksi hanya muncul sesuai status dan permission. Finalisasi tidak membuat jurnal; jurnal dibuat pada tahap Posting.</small></div><div class="table-responsive"><table class="table align-middle mb-0 payroll-table"><thead><tr><th>Periode</th><th>Lock Kehadiran</th><th>Status</th><th class="text-end">Karyawan</th><th class="text-end">Bruto</th><th class="text-end">Potongan</th><th class="text-end">Neto</th><th>Aksi Berikutnya</th></tr></thead><tbody>
<?php if(!$periods):?><tr><td colspan="8" class="text-center text-muted py-5">Belum ada periode payroll. Buat satu periode untuk memulai.</td></tr><?php endif;foreach($periods as$period):$start=$period->attendance_start_date?:$period->start_date;$end=$period->attendance_end_date?:$period->end_date;$locked=$period->attendance_lock_status==='locked';?>
<tr><td><b><?=html_escape($period->period_name)?></b><small class="d-block text-muted"><?=html_escape($start)?> s.d. <?=html_escape($end)?></small><small class="text-muted">Bayar: <?=html_escape($period->payment_date?:'-')?></small></td><td><span class="badge <?=$locked?'bg-success':'bg-secondary'?>"><?=$locked?'Terkunci':'Belum dikunci'?></span></td><td><span class="badge bg-<?=in_array($period->workflow_status,array('posted','paid'),true)?'success':($period->workflow_status==='draft'?'secondary':'warning')?>"><?=html_escape($statusLabels[$period->workflow_status]??$period->workflow_status)?></span></td><td class="text-end"><?=number_format($period->employee_count)?></td><td class="text-end">Rp <?=number_format($period->gross,2,',','.')?></td><td class="text-end">Rp <?=number_format($period->deductions,2,',','.')?></td><td class="text-end fw-bold">Rp <?=number_format($period->net,2,',','.')?></td><td><div class="payroll-actions"><button class="btn btn-sm btn-outline-primary payroll-detail" data-id="<?=$period->id?>"><i class="bi bi-eye"></i> Detail</button>
<?php if($permissions['post']&&$period->workflow_status==='draft'&&!$locked):?><form method="post" action="<?=base_url('hrpayroll/lock_attendance')?>" class="js-confirm" data-confirm-title="Kunci rentang kehadiran?" data-confirm-text="Absensi dalam rentang ini tidak dapat diubah setelah dikunci."><?=$csrf?><input type="hidden" name="period_id" value="<?=$period->id?>"><input type="hidden" name="notes" value="Validasi payroll <?=html_escape($period->period_name)?>"><button class="btn btn-sm btn-warning">Lock Kehadiran</button></form><?php endif?>
<?php if($permissions['create']&&$period->workflow_status==='draft'&&$locked):?><form method="post" action="<?=base_url('hrpayroll/calculate')?>" class="js-confirm" data-confirm-title="Hitung draft payroll?" data-confirm-text="Hasil Draft periode ini akan dibuat ulang tanpa menggandakan rincian."><?=$csrf?><input type="hidden" name="period_id" value="<?=$period->id?>"><button class="btn btn-sm btn-primary"><?=empty($period->calculated_at)?'Hitung Draft':'Hitung Ulang'?></button></form><?php endif?>
<?php if($permissions['approve']&&$period->workflow_status==='draft'&&!empty($period->calculated_at)):?><form method="post" action="<?=base_url('hrpayroll/workflow/'.$period->id.'/review')?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-warning">Review HR</button></form><?php endif?>
<?php if($permissions['approve']&&$period->workflow_status==='reviewed'):?><form method="post" action="<?=base_url('hrpayroll/workflow/'.$period->id.'/approve')?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-success">Approve</button></form><form method="post" action="<?=base_url('hrpayroll/workflow/'.$period->id.'/revert')?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-outline-secondary">Kembali ke Draft</button></form><?php endif?>
<?php if($permissions['approve']&&$period->workflow_status==='approved'):?><form method="post" action="<?=base_url('hrpayroll/workflow/'.$period->id.'/finalize')?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-dark">Finalisasi</button></form><form method="post" action="<?=base_url('hrpayroll/workflow/'.$period->id.'/revert')?>" class="js-confirm"><?=$csrf?><button class="btn btn-sm btn-outline-secondary">Kembali ke Draft</button></form><?php endif?>
<?php if($permissions['post']&&$period->workflow_status==='final'):?><form method="post" action="<?=base_url('hrpayroll/post/'.$period->id)?>" class="d-flex gap-1 js-confirm" data-confirm-title="Posting jurnal payroll?"><?=$csrf?><input class="form-control form-control-sm" type="date" name="date" value="<?=$period->payment_date?:date('Y-m-d')?>" required><button class="btn btn-sm btn-success">Posting</button></form><?php endif?>
<?php if($permissions['post']&&$period->workflow_status==='posted'):?><form method="post" action="<?=base_url('hrpayroll/pay/'.$period->id)?>" class="d-flex gap-1 js-confirm" data-confirm-title="Bayar payroll?"><?=$csrf?><input class="form-control form-control-sm" type="date" name="date" value="<?=$period->payment_date?:date('Y-m-d')?>" required><select class="form-select form-select-sm" name="cash_account_id" required><option value="">Kas/Bank</option><?php foreach($cash_accounts as$account):?><option value="<?=$account->id?>"><?=html_escape($account->code.' - '.$account->name)?></option><?php endforeach?></select><button class="btn btn-sm btn-success">Bayar</button></form><?php endif?>
<?php if($permissions['export']&&in_array($period->workflow_status,array('final','posted','paid'),true)):?><a class="btn btn-sm btn-outline-danger" href="<?=base_url('hrpayroll/export_pdf/'.$period->id)?>">PDF</a><a class="btn btn-sm btn-outline-success" href="<?=base_url('hrpayroll/export_excel/'.$period->id.'/general')?>">Excel</a><div class="dropdown"><button class="btn btn-sm btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown">Sumber Bank</button><div class="dropdown-menu"><a class="dropdown-item" href="<?=base_url('hrpayroll/export_excel/'.$period->id.'/bri')?>">BRI / QLola</a><a class="dropdown-item" href="<?=base_url('hrpayroll/export_excel/'.$period->id.'/bca')?>">BCA</a></div></div><?php endif?></div></td></tr><?php endforeach?>
</tbody></table></div></section>
<?php elseif($payroll_section==='salaries'):?>
<section class="card hr-panel"><div class="card-header bg-white"><b>Master Gaji Karyawan</b><small class="d-block text-muted">Estimasi hanya menjumlahkan komponen nominal tetap yang aktif saat ini; nilai final dihitung pada periode payroll.</small></div><div class="card-body border-bottom"><div class="row g-2"><div class="col-md-4"><label class="form-label">Departemen</label><select id="salaryDepartmentFilter" class="form-select"><option value="">Semua departemen</option><?php foreach($departments as$d):?><option><?=html_escape($d->department_name)?></option><?php endforeach?></select></div><div class="col-md-4"><label class="form-label">Posisi</label><select id="salaryPositionFilter" class="form-select"><option value="">Semua posisi</option><?php foreach($positions as$p):?><option><?=html_escape($p->position_name)?></option><?php endforeach?></select></div><div class="col-md-4"><label class="form-label">Status</label><select id="salaryStatusFilter" class="form-select"><option value="">Semua status</option><option>Aktif</option><option>Nonaktif</option></select></div></div></div><div class="table-responsive"><table id="salaryTable" class="table align-middle mb-0"><thead><tr><th>Karyawan</th><th>Departemen</th><th>Posisi</th><th>Status</th><th class="text-end">Pendapatan Tetap</th><th class="text-end">Potongan Tetap</th><th class="text-end">Estimasi Neto</th><th>Efektif</th><th>Aksi</th></tr></thead><tbody><?php foreach($salary_rows as$row):?><tr><td><b><?=html_escape($row->full_name)?></b><small class="d-block text-muted"><?=html_escape($row->employee_code)?></small></td><td><?=html_escape($row->department_name?:'-')?></td><td><?=html_escape($row->position_name?:'-')?></td><td><span class="badge <?=$row->is_active?'bg-success':'bg-secondary'?>"><?=$row->is_active?'Aktif':'Nonaktif'?></span><?php if($row->salary_restricted):?><span class="badge bg-danger">Gaji terbatas</span><?php endif?></td><td class="text-end">Rp <?=number_format($row->fixed_earnings,2,',','.')?></td><td class="text-end">Rp <?=number_format($row->fixed_deductions,2,',','.')?></td><td class="text-end fw-bold">Rp <?=number_format($row->estimated_net,2,',','.')?></td><td><?=html_escape($row->effective_from?:'-')?></td><td><?php if($permissions['update']):?><button class="btn btn-sm btn-outline-primary salary-manage" data-id="<?=$row->id?>"><i class="bi bi-sliders"></i> Atur Gaji</button><?php else:?><button class="btn btn-sm btn-outline-primary salary-manage" data-id="<?=$row->id?>">Detail</button><?php endif?></td></tr><?php endforeach?></tbody></table></div></section>
<?php elseif($payroll_section==='settings'):?>
<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="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 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>
<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%'});
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')})}
$('.salary-manage').on('click',function(){loadSalary(this.dataset.id)});$('#salaryLineForm').on('submit',function(e){e.preventDefault();const button=this.querySelector('button[type=submit]');button.disabled=true;$.ajax({url:'<?=base_url('hrpayroll/save_salary_line')?>',method:'POST',data:new FormData(this),processData:false,contentType:false,dataType:'json'}).done(r=>{if(!r.status)return AppAlert.toast(r.message,'error');AppAlert.toast(r.message,'success');this.reset();this.elements.employee_id.value=currentEmployee;loadSalary(currentEmployee)}).fail(x=>AppAlert.toast(x.responseJSON?.message||'Komponen gagal disimpan.','error')).always(()=>button.disabled=false)});$(document).on('click','.deactivate-salary',function(){if(!confirm('Nonaktifkan komponen tanpa menghapus historinya?'))return;const form=new FormData();form.append('<?=$this->security->get_csrf_token_name()?>','<?=$this->security->get_csrf_hash()?>');$.ajax({url:'<?=base_url('hrpayroll/deactivate_salary_line/')?>'+this.dataset.id,method:'POST',data:form,processData:false,contentType:false,dataType:'json'}).done(r=>{AppAlert.toast(r.message,r.status?'success':'error');if(r.status)loadSalary(currentEmployee)}).fail(x=>AppAlert.toast(x.responseJSON?.message||'Komponen gagal dinonaktifkan.','error'))});
});
</script>
@@ -1,5 +1,5 @@
<div class="container-fluid py-3">
<div class="mb-3"><h4 class="mb-1">Master Departemen & Posisi</h4><p class="text-muted mb-0">Satu sumber data untuk karyawan, Purchase Request, payroll, jadwal, dan modul lainnya.</p></div>
<?php $this->load->view('hr_payroll/_header',array('active_menu'=>'organization_master','title'=>'Departemen & Posisi','subtitle'=>'Satu sumber data organisasi untuk karyawan, pembelian, payroll, jadwal, dan modul terkait.')); ?>
<div class="row g-3">
<div class="col-lg-6"><div class="card h-100"><div class="card-header"><b>Departemen</b></div><div class="card-body">
<form id="departmentForm" class="row g-2 mb-3"><input type="hidden" name="<?=$this->security->get_csrf_token_name()?>" value="<?=$this->security->get_csrf_hash()?>"><input type="hidden" name="id"><div class="col-md-3"><input class="form-control" name="code" placeholder="Kode" required></div><div class="col-md-4"><input class="form-control" name="name" placeholder="Nama departemen" required></div><div class="col-md-5"><input class="form-control" name="description" placeholder="Keterangan"></div><div class="col-8"><label><input type="checkbox" name="is_active" value="1" checked> Aktif</label></div><div class="col-4 text-end"><button type="button" class="btn btn-light btn-reset">Reset</button> <button class="btn btn-primary">Simpan</button></div></form>
@@ -0,0 +1,4 @@
<!doctype html><html><head><meta charset="utf-8"><style>@page{margin:24px 28px}body{font:10px DejaVu Sans;color:#1f2937}h1{font-size:18px;margin:0}p{margin:3px 0}.head{display:table;width:100%;border-bottom:2px solid #ef7d00;padding-bottom:10px;margin-bottom:14px}.head>div{display:table-cell;vertical-align:top}.right{text-align:right}table{width:100%;border-collapse:collapse}th{background:#17284a;color:#fff;text-align:left}th,td{padding:7px;border:1px solid #dce3ed}.num{text-align:right}.total{font-weight:bold;background:#fff4e8}.foot{margin-top:14px;color:#64748b}</style></head><body>
<div class="head"><div><h1><?=html_escape($company->name??$company->company_name??'Perusahaan')?></h1><p>Register Payroll</p></div><div class="right"><b><?=html_escape($period->period_name)?></b><p>Kehadiran: <?=html_escape($period->attendance_start_date?:$period->start_date)?> s.d. <?=html_escape($period->attendance_end_date?:$period->end_date)?></p><p>Tanggal bayar: <?=html_escape($period->payment_date?:'-')?> · Status: <?=strtoupper(html_escape($period->workflow_status))?></p></div></div>
<table><thead><tr><th>No</th><th>Kode</th><th>Karyawan</th><th>Bank / Rekening</th><th class="num">Gaji Pokok</th><th class="num">Bruto</th><th class="num">Potongan</th><th class="num">Neto</th></tr></thead><tbody><?php $basic=$gross=$deduction=$net=0;foreach($rows as$i=>$r):$basic+=(float)$r->basic_salary;$gross+=(float)$r->gross_salary;$deduction+=(float)$r->total_deductions;$net+=(float)$r->total_salary;?><tr><td><?=$i+1?></td><td><?=html_escape($r->employee_code)?></td><td><?=html_escape($r->full_name)?></td><td><?=html_escape(trim(($r->bank_name?:'-').' / '.($r->bank_account_number?:'-'),' /'))?></td><td class="num"><?=number_format($r->basic_salary,2,',','.')?></td><td class="num"><?=number_format($r->gross_salary,2,',','.')?></td><td class="num"><?=number_format($r->total_deductions,2,',','.')?></td><td class="num"><?=number_format($r->total_salary,2,',','.')?></td></tr><?php endforeach?><tr class="total"><td colspan="4">TOTAL</td><td class="num"><?=number_format($basic,2,',','.')?></td><td class="num"><?=number_format($gross,2,',','.')?></td><td class="num"><?=number_format($deduction,2,',','.')?></td><td class="num"><?=number_format($net,2,',','.')?></td></tr></tbody></table>
<p class="foot">Dokumen payroll bersifat rahasia. Dibuat pada <?=date('d/m/Y H:i')?>.</p></body></html>
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Slip Gaji</title><style>body{font:14px Arial;max-width:760px;margin:30px auto}table{width:100%;border-collapse:collapse}td,th{padding:8px;border-bottom:1px solid #ddd}.r{text-align:right}@media print{button{display:none}}</style></head><body><button onclick="print()">Cetak</button><h2>SLIP GAJI</h2><p><b><?=html_escape($payroll->full_name)?></b> — <?=html_escape($payroll->employee_code)?><br>Periode <?=html_escape($payroll->period_name)?></p><table><tr><th>Komponen</th><th>Jenis</th><th class="r">Nilai</th></tr><?php foreach($lines as$l):?><tr><td><?=html_escape($l->component_name)?></td><td><?=$l->component_type?></td><td class="r"><?=number_format($l->amount,0,',','.')?></td></tr><?php endforeach?><tr><th colspan="2">Gaji Neto</th><th class="r"><?=number_format($payroll->total_salary,0,',','.')?></th></tr></table><small>Dokumen rahasia. Status payroll: <?=$payroll->workflow_status?></small></body></html>
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Slip Gaji</title><style>body{font:14px Arial;color:#1f2937;max-width:760px;margin:30px auto}.head{display:flex;justify-content:space-between;border-bottom:3px solid #ed7900;padding-bottom:12px}table{width:100%;border-collapse:collapse;margin-top:20px}td,th{padding:9px;border-bottom:1px solid #dce3ed}th{background:#f1f5f9;text-align:left}.r{text-align:right}.net{background:#17284a;color:#fff}.muted{color:#64748b;font-size:12px}@media print{button{display:none}}</style></head><body><button onclick="print()">Cetak</button><div class="head"><div><h2>SLIP GAJI</h2><b><?=html_escape($payroll->full_name)?></b> — <?=html_escape($payroll->employee_code)?></div><div class="r"><b><?=html_escape($payroll->period_name)?></b><div class="muted"><?=html_escape($payroll->attendance_start_date?:$payroll->period_start_date)?> s.d. <?=html_escape($payroll->attendance_end_date?:$payroll->period_end_date)?></div><div class="muted">Tanggal bayar: <?=html_escape($payroll->payment_date?:'-')?></div></div></div><table><tr><th>Komponen</th><th>Jenis</th><th class="r">Nilai</th></tr><?php foreach($lines as$line):?><tr><td><?=html_escape($line->component_name)?></td><td><?=html_escape($line->component_type)?></td><td class="r"><?=number_format($line->amount,2,',','.')?></td></tr><?php endforeach?><tr class="net"><th colspan="2">Gaji Neto</th><th class="r">Rp <?=number_format($payroll->total_salary,2,',','.')?></th></tr></table><p class="muted">Dokumen rahasia. Status payroll: <?=html_escape(strtoupper($payroll->workflow_status))?></p></body></html>