Files
2026-05-26 08:07:45 +00:00

831 lines
18 KiB
PHP

<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<!-- HEADER -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1">
Payroll Karyawan
</h4>
<small class="text-muted">
Generate dan kelola payroll karyawan
</small>
</div>
<div class="d-flex gap-2">
<input
type="month"
id="filterMonth"
class="form-control">
<button
class="btn btn-warning"
id="btnGeneratePayroll">
<i class="fa fa-refresh me-1"></i>
Generate Payroll
</button>
<button
class="btn btn-dark"
id="btnReload">
<i class="fa fa-search me-1"></i>
Filter
</button>
</div>
</div>
<!-- TABLE -->
<div class="table-responsive">
<table
id="tablePayroll"
class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="50">
No
</th>
<th>
Karyawan
</th>
<th>
Gaji Pokok
</th>
<th>
Pendapatan
</th>
<th>
Potongan
</th>
<th>
THP
</th>
<th width="180">
Status
</th>
<th width="180">
Aksi
</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- ========================================= -->
<!-- MODAL DETAIL PAYROLL -->
<!-- ========================================= -->
<div class="modal fade" id="modalPayroll">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content border-0 rounded-4">
<!-- HEADER -->
<div class="modal-header bg-warning text-white">
<div>
<h5 class="modal-title mb-0">
Detail Payroll
</h5>
<small id="employeeNameHeader"></small>
</div>
<button
class="btn-close"
data-bs-dismiss="modal">
</button>
</div>
<!-- BODY -->
<div class="modal-body">
<input type="hidden" id="payroll_id">
<div class="row">
<!-- LEFT -->
<div class="col-md-6">
<div class="card border-0 shadow-sm rounded-4 h-100">
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Pendapatan
</h5>
<button
class="btn btn-sm btn-success"
id="btnTambahPendapatan">
<i class="fa fa-plus"></i>
Tambah
</button>
</div>
<!-- BASIC SALARY -->
<div class="border rounded-4 p-3 mb-3 bg-light">
<div class="fw-bold">
Gaji Pokok
</div>
<div
class="text-success fw-bold"
id="basicSalaryText">
Rp 0
</div>
</div>
<!-- ITEMS -->
<div id="earningItems"></div>
</div>
</div>
</div>
<!-- RIGHT -->
<div class="col-md-6">
<div class="card border-0 shadow-sm rounded-4 h-100">
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Potongan
</h5>
<button
class="btn btn-sm btn-danger"
id="btnTambahPotongan">
<i class="fa fa-plus"></i>
Tambah
</button>
</div>
<!-- ITEMS -->
<div id="deductionItems"></div>
</div>
</div>
</div>
</div>
<!-- TAKE HOME PAY -->
<div class="card border-0 shadow-sm rounded-4 mt-4">
<div class="card-body">
<div class="row text-center">
<div class="col-md-4">
<small class="text-muted">
Total Pendapatan
</small>
<h5
class="text-success mt-1"
id="totalPendapatan">
Rp 0
</h5>
</div>
<div class="col-md-4">
<small class="text-muted">
Total Potongan
</small>
<h5
class="text-danger mt-1"
id="totalPotongan">
Rp 0
</h5>
</div>
<div class="col-md-4">
<small class="text-muted">
Take Home Pay
</small>
<h3
class="mt-1 fw-bold"
id="totalTakeHome">
Rp 0
</h3>
</div>
</div>
</div>
</div>
</div>
<!-- FOOTER -->
<div class="modal-footer">
<button
class="btn btn-secondary"
data-bs-dismiss="modal">
Tutup
</button>
<button
class="btn btn-warning"
id="btnSavePayroll">
<i class="fa fa-save me-1"></i>
Simpan Payroll
</button>
</div>
</div>
</div>
</div>
<!-- ========================================= -->
<!-- TEMPLATE ITEM -->
<!-- ========================================= -->
<script type="text/template" id="templatePayrollItem">
<div class="payroll-item border rounded-4 p-3 mb-3">
<div class="row">
<div class="col-md-5">
<label class="form-label">
Nama Item
</label>
<input
type="text"
class="form-control item-name"
placeholder="Contoh : Bonus Proyek">
</div>
<div class="col-md-3">
<label class="form-label">
Nominal
</label>
<input
type="number"
class="form-control item-amount"
value="0">
</div>
<div class="col-md-3">
<label class="form-label">
Tipe
</label>
<select class="form-control item-type">
<option value="allowance">
Tunjangan
</option>
<option value="bonus">
Bonus
</option>
<option value="overtime">
Lembur
</option>
<option value="deduction">
Potongan
</option>
<option value="bpjs">
BPJS
</option>
<option value="tax">
Pajak
</option>
</select>
</div>
<div class="col-md-1 d-flex align-items-end">
<button
class="btn btn-danger btn-remove-item w-100">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
</div>
</script>
<script>
$(function(){
// =========================================
// DEFAULT MONTH
// =========================================
let now = new Date();
let month =
now.getFullYear()
+ '-'
+ String(now.getMonth()+1).padStart(2,'0');
$('#filterMonth').val(month);
// =========================================
// DATATABLE
// =========================================
let table = $('#tablePayroll').DataTable({
processing:true,
serverSide:false,
ajax:{
url:"<?= base_url('payroll/get_data'); ?>",
type:"POST",
data:function(d){
d.month = $('#filterMonth').val();
}
},
columnDefs:[
{
targets:[2,3,4,5],
className:'text-end'
}
]
});
// =========================================
// RELOAD
// =========================================
$('#btnReload').click(function(){
table.ajax.reload(null,false);
});
// =========================================
// GENERATE PAYROLL
// =========================================
$('#btnGeneratePayroll').click(function(){
let month = $('#filterMonth').val();
if(!month){
Swal.fire(
'Warning',
'Pilih bulan terlebih dahulu',
'warning'
);
return;
}
Swal.fire({
title:'Generate Payroll?',
text:'Payroll akan dibuat otomatis',
icon:'question',
showCancelButton:true,
confirmButtonText:'Generate'
}).then((r)=>{
if(r.isConfirmed){
$.post(
"<?= base_url('payroll/generate_payroll'); ?>",
{
month:month
},
function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
'json'
);
}
});
});
// =========================================
// OPEN DETAIL
// =========================================
$(document).on(
'click',
'.btn-edit',
function(){
let id = $(this).data('id');
$('#payroll_id').val(id);
$('#earningItems').html('');
$('#deductionItems').html('');
$.get(
"<?= base_url('payroll/detail/'); ?>"+id,
function(res){
$('#employeeNameHeader').html(
res.employee.full_name
);
$('#basicSalaryText').html(
'Rp ' +
parseFloat(
res.payroll.basic_salary
).toLocaleString('id-ID')
);
// =========================
// LOAD ITEMS
// =========================
$.each(res.items,function(i,item){
appendItem(
item.item_name,
item.amount,
item.item_type
);
});
calculateTHP();
$('#modalPayroll').modal('show');
},
'json'
);
}
);
// =========================================
// ADD EARNING
// =========================================
$('#btnTambahPendapatan').click(function(){
appendItem(
'',
0,
'allowance'
);
});
// =========================================
// ADD DEDUCTION
// =========================================
$('#btnTambahPotongan').click(function(){
appendItem(
'',
0,
'deduction'
);
});
// =========================================
// APPEND ITEM
// =========================================
function appendItem(
name='',
amount=0,
type='allowance'
){
let html =
$('#templatePayrollItem').html();
let el = $(html);
el.find('.item-name').val(name);
el.find('.item-amount').val(amount);
el.find('.item-type').val(type);
if(
type == 'allowance'
||
type == 'bonus'
||
type == 'overtime'
){
$('#earningItems').append(el);
} else {
$('#deductionItems').append(el);
}
}
// =========================================
// REMOVE ITEM
// =========================================
$(document).on(
'click',
'.btn-remove-item',
function(){
$(this)
.closest('.payroll-item')
.remove();
calculateTHP();
}
);
// =========================================
// CALCULATE
// =========================================
$(document).on(
'keyup change',
'.item-amount',
function(){
calculateTHP();
}
);
function calculateTHP(){
let basicSalary =
parseFloat(
$('#basicSalaryText')
.text()
.replace(/[^0-9]/g,'')
) || 0;
let totalEarning = 0;
let totalDeduction = 0;
$('#earningItems .payroll-item').each(function(){
totalEarning += parseFloat(
$(this)
.find('.item-amount')
.val()
) || 0;
});
$('#deductionItems .payroll-item').each(function(){
totalDeduction += parseFloat(
$(this)
.find('.item-amount')
.val()
) || 0;
});
let thp =
(
basicSalary
+
totalEarning
)
-
totalDeduction;
$('#totalPendapatan').html(
'Rp '+
totalEarning.toLocaleString('id-ID')
);
$('#totalPotongan').html(
'Rp '+
totalDeduction.toLocaleString('id-ID')
);
$('#totalTakeHome').html(
'Rp '+
thp.toLocaleString('id-ID')
);
}
// =========================================
// SAVE PAYROLL
// =========================================
$('#btnSavePayroll').click(function(){
let items = [];
$('#earningItems .payroll-item').each(function(){
items.push({
item_name:
$(this)
.find('.item-name')
.val(),
amount:
$(this)
.find('.item-amount')
.val(),
item_type:
$(this)
.find('.item-type')
.val()
});
});
$('#deductionItems .payroll-item').each(function(){
items.push({
item_name:
$(this)
.find('.item-name')
.val(),
amount:
$(this)
.find('.item-amount')
.val(),
item_type:
$(this)
.find('.item-type')
.val()
});
});
$.post(
"<?= base_url('payroll/save_items'); ?>",
{
payroll_id:
$('#payroll_id').val(),
items:items
},
function(res){
if(res.status){
$('#modalPayroll').modal('hide');
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
'json'
);
});
});
</script>