Initial commit
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Accounts extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = [
|
||||
"active_menu" => "accounts",
|
||||
"parent_accounts" => $this->db->get('accounts')->result()
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('accounts/layout', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$this->dm->setTable('accounts')
|
||||
->setColumn(
|
||||
[null,'kode_akun','nama_akun','tipe','posisi','kategori',null],
|
||||
['kode_akun','nama_akun','tipe'],
|
||||
['kode_akun'=>'ASC']
|
||||
);
|
||||
|
||||
$list = $this->dm->get_datatables();
|
||||
|
||||
$data = [];
|
||||
$no = $_POST['start'];
|
||||
|
||||
foreach ($list as $row) {
|
||||
$no++;
|
||||
|
||||
$data[] = [
|
||||
$no,
|
||||
$row->kode_akun,
|
||||
$row->nama_akun,
|
||||
ucfirst($row->tipe),
|
||||
ucfirst($row->posisi),
|
||||
ucfirst($row->kategori),
|
||||
'
|
||||
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"draw" => $_POST['draw'],
|
||||
"recordsTotal" => $this->dm->count_all(),
|
||||
"recordsFiltered" => $this->dm->count_filtered(),
|
||||
"data" => $data
|
||||
]);
|
||||
}
|
||||
|
||||
// ================= SAVE
|
||||
public function save()
|
||||
{
|
||||
$tipe = $this->input->post('tipe');
|
||||
|
||||
$data = [
|
||||
'kode_akun' => $this->input->post('kode_akun'),
|
||||
'nama_akun' => $this->input->post('nama_akun'),
|
||||
'tipe' => $tipe,
|
||||
'posisi' => in_array($tipe, ['asset','expense']) ? 'debit' : 'kredit',
|
||||
'kategori' => in_array($tipe, ['asset','liability','equity']) ? 'neraca' : 'laba_rugi',
|
||||
'parent_id' => $this->input->post('parent_id') ?: NULL
|
||||
];
|
||||
|
||||
$insert = $this->dm->setTable('accounts')->insert($data);
|
||||
|
||||
if($insert){
|
||||
log_activity(
|
||||
'Accounts',
|
||||
'create',
|
||||
'Menambah akun: '.$data['kode_akun'].' - '.$data['nama_akun']
|
||||
);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => $insert ? true : false,
|
||||
'message' => $insert ? 'Data berhasil disimpan' : 'Gagal simpan'
|
||||
]);
|
||||
}
|
||||
|
||||
// ================= UPDATE
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$tipe = $this->input->post('tipe');
|
||||
|
||||
$data = [
|
||||
'kode_akun' => $this->input->post('kode_akun'),
|
||||
'nama_akun' => $this->input->post('nama_akun'),
|
||||
'tipe' => $tipe,
|
||||
'posisi' => in_array($tipe, ['asset','expense']) ? 'debit' : 'kredit',
|
||||
'kategori' => in_array($tipe, ['asset','liability','equity']) ? 'neraca' : 'laba_rugi',
|
||||
'parent_id' => $this->input->post('parent_id') ?: NULL
|
||||
];
|
||||
|
||||
$update = $this->dm->setTable('accounts')->update($id, $data);
|
||||
|
||||
if($update){
|
||||
log_activity(
|
||||
'Accounts',
|
||||
'update',
|
||||
'Update akun ID '.$id.' menjadi: '.$data['kode_akun'].' - '.$data['nama_akun']
|
||||
);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => $update ? true : false,
|
||||
'message' => $update ? 'Data berhasil diupdate' : 'Gagal update'
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail($id)
|
||||
{
|
||||
$data = $this->dm->setTable('accounts')->find($id);
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
// cek apakah jadi parent
|
||||
$child = $this->db->where('parent_id',$id)->count_all_results('accounts');
|
||||
|
||||
if($child > 0){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Tidak bisa hapus, akun punya child'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$delete = $this->dm->setTable('accounts')->delete($id);
|
||||
|
||||
if($delete){
|
||||
log_activity(
|
||||
'Accounts',
|
||||
'delete',
|
||||
'Menghapus akun ID '.$id,
|
||||
'success'
|
||||
);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => $delete ? true : false,
|
||||
'message' => $delete ? 'Data berhasil dihapus' : 'Gagal hapus'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Asset extends CI_Controller {
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
$this->load->database();
|
||||
}
|
||||
|
||||
public function index(){
|
||||
$data = ["active_menu" => "asset"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('asset/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// ================= GET DATA =================
|
||||
public function get_data(){
|
||||
$data = $this->db
|
||||
->select('assets.*, lokasi_asset.nama as nama_lokasi')
|
||||
->from('assets')
|
||||
->join('lokasi_asset', 'lokasi_asset.id = assets.lokasi_asset_id', 'left')
|
||||
->order_by('assets.id','DESC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
$no = 1;
|
||||
$result = [];
|
||||
|
||||
foreach($data as $d){
|
||||
$result[] = [
|
||||
$no++,
|
||||
$d->tanggal_perolehan,
|
||||
$d->kode_asset ?? '-',
|
||||
$d->nama_asset,
|
||||
$d->nama_lokasi ?? '-',
|
||||
number_format($d->nilai_perolehan),
|
||||
$d->masa_manfaat . ' bln',
|
||||
number_format($d->penyusutan_per_bulan),
|
||||
$d->created_at,
|
||||
'
|
||||
<button class="btn btn-sm btn-info btn-detail mb-1 ml-1" data-id="'.$d->id.'"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-sm btn-warning btn-edit mb-1 ml-1" data-id="'.$d->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete mb-1 ml-1" data-id="'.$d->id.'">Hapus</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(['data'=>$result]);
|
||||
}
|
||||
|
||||
// ================= DETAIL ASSET + HISTORY =================
|
||||
public function detail($id)
|
||||
{
|
||||
$data = $this->db
|
||||
->select('a.*, la.nama AS nama_lokasi') // ambil field tambahan dari lokasi_asset
|
||||
->from('assets a')
|
||||
->join('lokasi_asset la', 'a.lokasi_asset_id = la.id', 'left')
|
||||
->where('a.id', $id)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function get_history($asset_id){
|
||||
$data = $this->db
|
||||
->where('asset_id', $asset_id)
|
||||
->order_by('created_at', 'DESC')
|
||||
->get('asset_mutations')
|
||||
->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// ================= SAVE MANUAL (1 UNIT) =================
|
||||
public function save(){
|
||||
$harga = preg_replace('/\D/','',$this->input->post('harga'));
|
||||
$residu = preg_replace('/\D/','',$this->input->post('residu'));
|
||||
$masa = (int)$this->input->post('masa');
|
||||
$account_debit_id = (int)$this->input->post('account_asset');
|
||||
$account_kredit_id = (int)$this->input->post('account_kas');
|
||||
$lokasi_asset_id = (int)$this->input->post('lokasi_asset');
|
||||
$tanggal = $this->input->post('tanggal');
|
||||
$nama_asset = $this->input->post('nama_asset');
|
||||
$keterangan = $this->input->post('keterangan');
|
||||
|
||||
$nilai_perolehan = $harga * 1;
|
||||
$penyusutan = ($harga - $residu) / ($masa ?: 1);
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
$data = [
|
||||
'kode_asset' => 'AST-'.time(),
|
||||
'nama_asset' => $nama_asset,
|
||||
'harga_per_unit' => $harga,
|
||||
'nilai_perolehan' => $nilai_perolehan,
|
||||
'tanggal_perolehan' => $tanggal,
|
||||
'masa_manfaat' => $masa,
|
||||
'nilai_residu' => $residu,
|
||||
'penyusutan_per_bulan' => $penyusutan,
|
||||
'nilai_buku' => $nilai_perolehan,
|
||||
'sumber' => 'manual',
|
||||
'account_debit_id' => $account_debit_id,
|
||||
'account_kredit_id' => $account_kredit_id,
|
||||
'lokasi_asset_id' => $lokasi_asset_id,
|
||||
'keterangan' => $keterangan
|
||||
];
|
||||
|
||||
$this->db->insert('assets',$data);
|
||||
$asset_id = $this->db->insert_id();
|
||||
|
||||
// Journal
|
||||
$this->db->insert('journals',[
|
||||
'tanggal' => $tanggal,
|
||||
'no_ref' => 'AST-'.date('YmdHis').'-'.$asset_id,
|
||||
'keterangan' => 'Penambahan Aset '.$nama_asset,
|
||||
'ref_type' => 'assets',
|
||||
'ref_id' => $asset_id,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id' => $jid,
|
||||
'account_id' => $account_debit_id,
|
||||
'debit' => $nilai_perolehan,
|
||||
'kredit' => 0
|
||||
]);
|
||||
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id' => $jid,
|
||||
'account_id' => $account_kredit_id,
|
||||
'debit' => 0,
|
||||
'kredit' => $nilai_perolehan
|
||||
]);
|
||||
|
||||
// MUTATION PEROLEHAN
|
||||
$this->db->insert('asset_mutations',[
|
||||
'asset_id' => $asset_id,
|
||||
'tipe' => 'perolehan',
|
||||
'nilai' => $nilai_perolehan,
|
||||
'keterangan' => 'Perolehan manual: '.$keterangan
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
echo json_encode(['status'=>true]);
|
||||
}
|
||||
|
||||
// ================= SAVE FROM STOCK =================
|
||||
public function save_from_stock(){
|
||||
$item_id = $this->input->post('item_id');
|
||||
$account_debit_id = (int)$this->input->post('account_asset');
|
||||
$lokasi_asset_id = (int)$this->input->post('s_lokasi_asset');
|
||||
$masa = (int)$this->input->post('s_masa');
|
||||
$residu = preg_replace('/\D/','',$this->input->post('s_residu'));
|
||||
$tanggal = $this->input->post('s_tanggal');
|
||||
|
||||
$item = $this->db->get_where('items',['id'=>$item_id])->row();
|
||||
|
||||
if(!$item){
|
||||
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
if($item->stok < 1){
|
||||
echo json_encode(['status'=>false,'message'=>'Stok tidak cukup']);
|
||||
return;
|
||||
}
|
||||
|
||||
$harga = $item->harga_beli;
|
||||
$nama_asset = $item->nama_barang;
|
||||
$nilai_perolehan = $harga;
|
||||
$penyusutan = ($harga - $residu) / ($masa ?: 1);
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// INSERT ASSET
|
||||
$this->db->insert('assets',[
|
||||
'item_id' => $item_id,
|
||||
'kode_asset' => 'AST-'.time(),
|
||||
'nama_asset' => $nama_asset,
|
||||
'harga_per_unit' => $harga,
|
||||
'nilai_perolehan' => $nilai_perolehan,
|
||||
'tanggal_perolehan' => $tanggal,
|
||||
'masa_manfaat' => $masa,
|
||||
'nilai_residu' => $residu,
|
||||
'penyusutan_per_bulan' => $penyusutan,
|
||||
'nilai_buku' => $nilai_perolehan,
|
||||
'sumber' => 'gudang',
|
||||
'account_debit_id' => $account_debit_id,
|
||||
'account_kredit_id' => 21, // Persediaan
|
||||
'lokasi_asset_id' => $lokasi_asset_id
|
||||
]);
|
||||
|
||||
$asset_id = $this->db->insert_id();
|
||||
|
||||
// Journal
|
||||
$this->db->insert('journals',[
|
||||
'tanggal' => $tanggal,
|
||||
'no_ref' => 'AST-'.date('YmdHis').'-'.$asset_id,
|
||||
'keterangan' => 'Penambahan Aset dari Gudang '.$nama_asset,
|
||||
'ref_type' => 'assets',
|
||||
'ref_id' => $asset_id,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
// Debit Asset
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id' => $jid,
|
||||
'account_id' => $account_debit_id,
|
||||
'debit' => $nilai_perolehan,
|
||||
'kredit' => 0
|
||||
]);
|
||||
|
||||
// Kredit Persediaan
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id' => $jid,
|
||||
'account_id' => 21,
|
||||
'debit' => 0,
|
||||
'kredit' => $nilai_perolehan
|
||||
]);
|
||||
|
||||
// UPDATE STOK ITEM (kurangi 1)
|
||||
$this->db->where('id', $item_id)
|
||||
->set('stok', 'stok-1', FALSE)
|
||||
->update('items');
|
||||
|
||||
// MUTATION DARI GUDANG
|
||||
$this->db->insert('asset_mutations',[
|
||||
'asset_id' => $asset_id,
|
||||
'tipe' => 'perolehan',
|
||||
'nilai' => $nilai_perolehan,
|
||||
'keterangan' => 'Perolehan dari gudang: '.$nama_asset
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
echo json_encode(['status'=>true]);
|
||||
}
|
||||
|
||||
// ================= UPDATE =================
|
||||
public function update(){
|
||||
$id = $this->input->post('id');
|
||||
$nama_asset = $this->input->post('nama_asset');
|
||||
$lokasi_asset_id = (int)$this->input->post('lokasi_asset');
|
||||
$keterangan = $this->input->post('keterangan');
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
$data = [
|
||||
'nama_asset' => $nama_asset,
|
||||
'lokasi_asset_id' => $lokasi_asset_id,
|
||||
'keterangan' => $keterangan
|
||||
];
|
||||
|
||||
$this->db->where('id',$id);
|
||||
$this->db->update('assets',$data);
|
||||
|
||||
// MUTATION EDIT
|
||||
$this->db->insert('asset_mutations',[
|
||||
'asset_id' => $id,
|
||||
'tipe' => 'penambahan',
|
||||
'nilai' => 0,
|
||||
'keterangan' => 'Edit asset: '.$nama_asset
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
echo json_encode(['status'=>true]);
|
||||
}
|
||||
|
||||
// ================= DELETE =================
|
||||
public function delete(){
|
||||
$id = $this->input->post('id');
|
||||
$alasan = $this->input->post('alasan');
|
||||
$keterangan = $this->input->post('keterangan');
|
||||
|
||||
$asset = $this->db->get_where('assets',['id'=>$id])->row();
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// Hapus journal
|
||||
$journals = $this->db->get_where('journals', ['ref_type'=>'assets', 'ref_id'=>$id])->result();
|
||||
foreach($journals as $j){
|
||||
$this->db->delete('journal_details', ['journal_id'=>$j->id]);
|
||||
$this->db->delete('journals', ['id'=>$j->id]);
|
||||
}
|
||||
|
||||
// Hapus asset
|
||||
$this->db->delete('assets',['id'=>$id]);
|
||||
|
||||
// MUTATION DELETE
|
||||
$this->db->insert('asset_mutations',[
|
||||
'asset_id' => $id,
|
||||
'tipe' => 'pengurangan',
|
||||
'nilai' => $asset->nilai_buku,
|
||||
'keterangan' => 'Penghapusan ('.$alasan.'): '.$keterangan
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
echo json_encode(['status'=>true]);
|
||||
}
|
||||
|
||||
// ================= GET DATA SELECT =================
|
||||
public function get_accounts_kas(){
|
||||
$data = $this->db
|
||||
->where_in('sub_tipe', ['kas', 'hutang'])
|
||||
->order_by('kode_akun', 'ASC')
|
||||
->get('accounts')
|
||||
->result();
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function get_accounts_asset(){
|
||||
$data = $this->db
|
||||
->group_start()
|
||||
->like('nama_akun', 'Aset', 'after')
|
||||
->group_end()
|
||||
->order_by('kode_akun', 'ASC')
|
||||
->get('accounts')
|
||||
->result();
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function get_items(){
|
||||
$data = $this->db
|
||||
->where('stok > 0')
|
||||
->get('items')
|
||||
->result();
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function get_lokasi_asset(){
|
||||
$data = $this->db
|
||||
->get('lokasi_asset')
|
||||
->result();
|
||||
echo json_encode($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class AttendanceWebhook extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
}
|
||||
|
||||
public function cdata()
|
||||
{
|
||||
// =========================
|
||||
// AMBIL SN MESIN
|
||||
// =========================
|
||||
$sn = $this->input->get('SN');
|
||||
|
||||
// =========================
|
||||
// RAW BODY DARI MESIN
|
||||
// =========================
|
||||
$body = file_get_contents('php://input');
|
||||
|
||||
// =========================
|
||||
// LOG DIRECTORY
|
||||
// =========================
|
||||
$logDir = APPPATH . 'logs/fingerprint/';
|
||||
|
||||
if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0777, true);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// FILE LOG
|
||||
// =========================
|
||||
$logFile = $logDir . date('Y-m-d') . '.log';
|
||||
|
||||
// =========================
|
||||
// FORMAT LOG HEADER
|
||||
// =========================
|
||||
$logText = "\n==================================================\n";
|
||||
$logText .= "TIME : " . date('Y-m-d H:i:s') . "\n";
|
||||
$logText .= "SN : " . $sn . "\n";
|
||||
$logText .= "IP : " . $this->input->ip_address() . "\n";
|
||||
$logText .= "DATA : \n";
|
||||
|
||||
// =========================
|
||||
// PARSE BARIS ABSENSI
|
||||
// =========================
|
||||
$lines = explode("\n", trim($body));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
|
||||
$line = trim($line);
|
||||
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$logText .= $line . "\n";
|
||||
|
||||
// contoh parsing
|
||||
$data = explode("\t", $line);
|
||||
|
||||
/*
|
||||
hasil biasanya:
|
||||
[0] => PIN/User ID
|
||||
[1] => Datetime
|
||||
[2] => Status
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SIMPAN LOG
|
||||
// =========================
|
||||
file_put_contents($logFile, $logText, FILE_APPEND);
|
||||
|
||||
// =========================
|
||||
// RESPONSE KE MESIN
|
||||
// =========================
|
||||
echo "OK";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Attendancemonitoring extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "attendance_monitoring"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/attendance_monitoring');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$date = $this->input->get('date');
|
||||
|
||||
if(!$date){
|
||||
$date = date('Y-m-d');
|
||||
}
|
||||
|
||||
$data = $this->db->query("
|
||||
SELECT
|
||||
a.*,
|
||||
e.employee_code,
|
||||
e.full_name,
|
||||
s.shift_name
|
||||
FROM k_attendances a
|
||||
LEFT JOIN k_employees e ON e.id = a.employee_id
|
||||
LEFT JOIN k_shifts s ON s.id = a.shift_id
|
||||
WHERE a.attendance_date = '$date'
|
||||
ORDER BY a.checkin_time DESC
|
||||
")->result();
|
||||
|
||||
$rows = [];
|
||||
$no = 1;
|
||||
|
||||
foreach($data as $d){
|
||||
|
||||
$rows[] = [
|
||||
$no++,
|
||||
$d->employee_code,
|
||||
$d->full_name,
|
||||
$d->shift_name,
|
||||
$d->checkin_time,
|
||||
$d->checkout_time,
|
||||
$d->attendance_status
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'data' => $rows
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Auth extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if ($this->session->userdata('logged_in')) {
|
||||
redirect('dashboard');
|
||||
}
|
||||
|
||||
$this->load->view('auth/login');
|
||||
}
|
||||
|
||||
public function process_login()
|
||||
{
|
||||
$username = $this->input->post('username');
|
||||
$password = $this->input->post('password');
|
||||
|
||||
$user = $this->db->get_where('v_users', ['username' => $username])->row();
|
||||
|
||||
if ($user && password_verify($password, $user->password)) {
|
||||
|
||||
// Decode permissions
|
||||
$permissions = json_decode($user->permissions, true);
|
||||
if (!is_array($permissions)) {
|
||||
$permissions = [];
|
||||
}
|
||||
|
||||
$session_data = [
|
||||
'user_id' => $user->id,
|
||||
'nama' => $user->nama,
|
||||
'username' => $user->username,
|
||||
'role' => $user->nama_role,
|
||||
'permissions' => $permissions,
|
||||
'logged_in' => TRUE
|
||||
];
|
||||
|
||||
$this->session->set_userdata($session_data);
|
||||
|
||||
// 🔥 LOG LOGIN BERHASIL
|
||||
log_activity(
|
||||
'Auth',
|
||||
'login',
|
||||
'User '.$user->username.' berhasil login',
|
||||
'success'
|
||||
);
|
||||
|
||||
redirect('dashboard');
|
||||
|
||||
} else {
|
||||
|
||||
// 🔥 LOG LOGIN GAGAL
|
||||
log_activity(
|
||||
'Auth',
|
||||
'login_failed',
|
||||
'Login gagal untuk username: '.$username,
|
||||
'failed'
|
||||
);
|
||||
|
||||
$this->session->set_flashdata('error', 'Username atau Password salah!');
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
$username = $this->session->userdata('username');
|
||||
|
||||
// 🔥 LOG LOGOUT (SEBELUM DESTROY SESSION)
|
||||
log_activity(
|
||||
'Auth',
|
||||
'logout',
|
||||
'User '.$username.' logout',
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->session->sess_destroy();
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Basejurnal extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
// ================= INDEX
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = [
|
||||
"active_menu" => "basejurnal"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('basejurnal/layout', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// ================= DATATABLE
|
||||
public function get_data()
|
||||
{
|
||||
$this->dm->setTable('base_journal')
|
||||
->setColumn(
|
||||
[null,'kode','nama','deskripsi','is_active',null],
|
||||
['kode','nama'],
|
||||
['id'=>'DESC']
|
||||
);
|
||||
|
||||
$list = $this->dm->get_datatables();
|
||||
|
||||
$data = [];
|
||||
$no = $_POST['start'];
|
||||
|
||||
foreach ($list as $row) {
|
||||
$no++;
|
||||
|
||||
$status = $row->is_active ? 'Aktif' : 'Nonaktif';
|
||||
|
||||
$data[] = [
|
||||
$no,
|
||||
$row->kode,
|
||||
$row->nama,
|
||||
$row->deskripsi,
|
||||
$status,
|
||||
'
|
||||
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Hapus</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"draw" => $_POST['draw'],
|
||||
"recordsTotal" => $this->dm->count_all(),
|
||||
"recordsFiltered" => $this->dm->count_filtered(),
|
||||
"data" => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->db->trans_start();
|
||||
|
||||
$kode = trim($this->input->post('kode'));
|
||||
$nama = trim($this->input->post('nama'));
|
||||
|
||||
if(empty($kode) || empty($nama)){
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Kode & Nama wajib diisi'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$header = [
|
||||
'kode' => $kode,
|
||||
'nama' => $nama,
|
||||
'deskripsi' => $this->input->post('deskripsi'),
|
||||
'is_active' => 1
|
||||
];
|
||||
|
||||
$this->dm->setTable('base_journal')->insert($header);
|
||||
$base_id = $this->db->insert_id();
|
||||
|
||||
$accounts = $this->input->post('account_id');
|
||||
$posisi = $this->input->post('posisi');
|
||||
|
||||
if(!empty($accounts)){
|
||||
$detail = [];
|
||||
|
||||
foreach ($accounts as $i => $acc) {
|
||||
if(empty($acc)) continue;
|
||||
|
||||
$detail[] = [
|
||||
'base_journal_id' => $base_id,
|
||||
'account_id' => $acc,
|
||||
'posisi' => $posisi[$i] ?? 'debit',
|
||||
'urutan' => $i + 1
|
||||
];
|
||||
}
|
||||
|
||||
if(!empty($detail)){
|
||||
$this->db->insert_batch('base_journal_detail', $detail);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if (!$this->db->trans_status()) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Gagal simpan data'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
log_activity(
|
||||
'base_journal',
|
||||
'create',
|
||||
'Membuat base journal kode: '.$kode,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Data berhasil disimpan'
|
||||
]);
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
|
||||
if(empty($id)){
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'ID tidak valid'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
$header = [
|
||||
'kode' => trim($this->input->post('kode')),
|
||||
'nama' => trim($this->input->post('nama')),
|
||||
'deskripsi' => $this->input->post('deskripsi'),
|
||||
];
|
||||
|
||||
$this->dm->setTable('base_journal')->update($id, $header);
|
||||
|
||||
// 🔥 DELETE OLD DETAIL
|
||||
$this->db->where('base_journal_id', $id)->delete('base_journal_detail');
|
||||
|
||||
// 🔥 INSERT NEW DETAIL
|
||||
$accounts = $this->input->post('account_id');
|
||||
$posisi = $this->input->post('posisi');
|
||||
|
||||
if(!empty($accounts)){
|
||||
$detail = [];
|
||||
|
||||
foreach ($accounts as $i => $acc) {
|
||||
if(empty($acc)) continue;
|
||||
|
||||
$detail[] = [
|
||||
'base_journal_id' => $id,
|
||||
'account_id' => $acc,
|
||||
'posisi' => $posisi[$i] ?? 'debit',
|
||||
'urutan' => $i + 1
|
||||
];
|
||||
}
|
||||
|
||||
if(!empty($detail)){
|
||||
$this->db->insert_batch('base_journal_detail', $detail);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if (!$this->db->trans_status()) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Gagal update data'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
log_activity(
|
||||
'base_journal',
|
||||
'update',
|
||||
'Mengubah base journal ID: '.$id.' (kode: '.$header['kode'].')',
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Data berhasil diupdate'
|
||||
]);
|
||||
}
|
||||
|
||||
// ================= DETAIL
|
||||
public function detail($id)
|
||||
{
|
||||
$header = $this->dm->setTable('base_journal')->find($id);
|
||||
|
||||
$detail = $this->db
|
||||
->select('d.*, a.nama_akun as account_name')
|
||||
->from('base_journal_detail d')
|
||||
->join('accounts a','a.id = d.account_id','left') // <-- GANTI DI SINI
|
||||
->where('d.base_journal_id',$id)
|
||||
->order_by('d.urutan','ASC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
echo json_encode([
|
||||
'header' => $header,
|
||||
'detail' => $detail
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete($id = null)
|
||||
{
|
||||
// set response JSON
|
||||
$this->output->set_content_type('application/json');
|
||||
|
||||
if (empty($id)) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'ID tidak valid'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// hapus detail dulu
|
||||
$this->db->where('base_journal_id', $id)->delete('base_journal_detail');
|
||||
|
||||
// hapus header
|
||||
$delete = $this->dm->setTable('base_journal')->delete($id);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if (!$this->db->trans_status() || !$delete) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Gagal hapus data'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
log_activity(
|
||||
'base_journal',
|
||||
'delete',
|
||||
'Menghapus base journal ID: '.$id,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Data berhasil dihapus'
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_list_account()
|
||||
{
|
||||
$data = $this->db->select('id, nama_akun, kode_akun')
|
||||
->from('accounts') // sesuaikan tabel kamu
|
||||
->order_by('kode_akun','ASC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Bukubesar extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "buku_besar"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('buku_besar/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET ACCOUNTS (FILTER)
|
||||
// =========================
|
||||
public function get_accounts()
|
||||
{
|
||||
$data = $this->dm->setTable('accounts')
|
||||
->order_by('kode_akun','ASC')
|
||||
->get();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA BUKU BESAR
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$account_id = $this->input->get('account_id');
|
||||
$start = $this->input->get('start_date');
|
||||
$end = $this->input->get('end_date');
|
||||
|
||||
$this->db->select('
|
||||
journals.tanggal,
|
||||
journals.no_ref,
|
||||
journals.keterangan,
|
||||
journal_details.debit,
|
||||
journal_details.kredit,
|
||||
accounts.nama_akun,
|
||||
accounts.tipe
|
||||
');
|
||||
$this->db->from('journal_details');
|
||||
$this->db->join('journals','journals.id = journal_details.journal_id');
|
||||
$this->db->join('accounts','accounts.id = journal_details.account_id');
|
||||
|
||||
if ($account_id) {
|
||||
$this->db->where('journal_details.account_id', $account_id);
|
||||
}
|
||||
|
||||
if ($start && $end) {
|
||||
$this->db->where('journals.tanggal >=', $start);
|
||||
$this->db->where('journals.tanggal <=', $end);
|
||||
}
|
||||
// 🔥 FILTER DEBIT/KREDIT TIDAK BOLEH 0 SEMUA
|
||||
$this->db->group_start();
|
||||
$this->db->where('journal_details.debit >', 0);
|
||||
$this->db->or_where('journal_details.kredit >', 0);
|
||||
$this->db->group_end();
|
||||
|
||||
$this->db->order_by('journals.tanggal','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
// =========================
|
||||
// HITUNG SALDO BERJALAN
|
||||
// =========================
|
||||
$saldo = 0;
|
||||
$result = [];
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
// LOGIKA SALDO BERDASARKAN TIPE AKUN
|
||||
if (in_array($r->tipe, ['asset','expense'])) {
|
||||
$saldo += ($r->debit - $r->kredit);
|
||||
} else {
|
||||
$saldo += ($r->kredit - $r->debit);
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'tanggal' => $r->tanggal,
|
||||
'no_ref' => $r->no_ref,
|
||||
'keterangan' => $r->keterangan,
|
||||
'debit' => $r->debit,
|
||||
'kredit' => $r->kredit,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Cameraqrscan extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
// Cek apakah user sudah login
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('login');
|
||||
}
|
||||
}
|
||||
|
||||
public function index($type = 'masuk')
|
||||
{
|
||||
// pastikan hanya masuk/keluar
|
||||
$type = strtolower($type);
|
||||
if (!in_array($type, ['masuk', 'keluar'])) {
|
||||
show_404();
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
"active_menu" => "",
|
||||
"scan_type" => $type // passing ke view
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('scan/qr_scan', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$qr_code = $this->input->post('qr_code');
|
||||
$scan_type = $this->input->post('scan_type'); // masuk / keluar
|
||||
|
||||
if (!$qr_code) {
|
||||
echo json_encode(['status' => false, 'message' => 'QR Code tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// AMBIL DATA SISWA
|
||||
// ============================================================
|
||||
$siswa = $this->db->get_where('v_siswa', ['qr_id' => $qr_code])->row_array();
|
||||
|
||||
if (!$siswa) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Data siswa tidak ditemukan'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$id_siswa = $siswa['id'];
|
||||
$id_kelas = $siswa['id_kelas'];
|
||||
$id_kamar = $siswa['id_kamar'];
|
||||
|
||||
// ============================================================
|
||||
// CEGAH DUPLIKAT 5 MENIT (PostgreSQL)
|
||||
// ============================================================
|
||||
// Hitung waktu 5 menit yang lalu
|
||||
$datetimeNow = date('Y-m-d H:i:s');
|
||||
$datetime5MenitLalu = date('Y-m-d H:i:s', strtotime('-5 minutes'));
|
||||
|
||||
$cek = $this->db->query("
|
||||
SELECT id
|
||||
FROM kunjungan
|
||||
WHERE id_siswa = ?
|
||||
AND created_at >= ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1
|
||||
", [$id_siswa, $datetime5MenitLalu])->row_array();
|
||||
|
||||
if ($cek) {
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'data' => $siswa,
|
||||
'scan_type' => $scan_type,
|
||||
'message' => "Sudah tercatat dalam 5 menit terakhir"
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// AMBIL TEMPLATE PESAN
|
||||
// ============================================================
|
||||
$template = $this->db->get_where('template_pesan', ['jenis' => $scan_type])->row_array();
|
||||
|
||||
if (!$template) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Template pesan tidak ditemukan => ' . $scan_type
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$pesan = $template['isi_template'];
|
||||
$pesanType = $template['type'];
|
||||
|
||||
|
||||
// ============================================================
|
||||
// REPLACE TEMPLATE {{column}}
|
||||
// ============================================================
|
||||
foreach ($siswa as $key => $value) {
|
||||
$pesan = str_replace('{{'.$key.'}}', $value, $pesan);
|
||||
}
|
||||
|
||||
// Tambahan otomatis
|
||||
$pesan = str_replace('{{tanggal}}', date('Y-m-d'), $pesan);
|
||||
$pesan = str_replace('{{jam}}', date('H:i:s'), $pesan);
|
||||
|
||||
|
||||
// ============================================================
|
||||
// INSERT KUNJUNGAN
|
||||
// ============================================================
|
||||
$this->db->insert('kunjungan', [
|
||||
'id_siswa' => $id_siswa,
|
||||
'type' => $scan_type,
|
||||
'id_users' => $this->session->userdata('user_id'),
|
||||
'id_kelas' => $id_kelas,
|
||||
'id_kamar' => $id_kamar,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'media_type' => 'qr'
|
||||
]);
|
||||
|
||||
$new_id = $this->db->insert_id();
|
||||
|
||||
|
||||
// ============================================================
|
||||
// INSERT NOTIFIKASI WA
|
||||
// ============================================================
|
||||
$this->db->insert('notifikasi', [
|
||||
'type' => $pesanType,
|
||||
'pesan' => $pesan,
|
||||
'status' => 'pending',
|
||||
'nomor_whatsapp' => $siswa['nomor_whatsapp'],
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
|
||||
// ============================================================
|
||||
// RESPONSE
|
||||
// ============================================================
|
||||
$info = ($scan_type === 'masuk')
|
||||
? "Berhasil scan MASUK"
|
||||
: "Berhasil scan KELUAR";
|
||||
|
||||
// tampilkan data lengkap dari v_kunjungan
|
||||
$dataSiswa = $this->db->get_where('v_kunjungan_detail', ['id' => $new_id])->row();
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'data' => $dataSiswa,
|
||||
'scan_type' => $scan_type,
|
||||
'message' => $info
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Cronjob extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->database();
|
||||
|
||||
// ONLY CLI ACCESS
|
||||
if (!$this->input->is_cli_request()) {
|
||||
exit('No direct access allowed');
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_insight()
|
||||
{
|
||||
$period = date('Y-m');
|
||||
|
||||
// =====================================================
|
||||
// 🔥 REPLACE MODE (hapus data lama dulu)
|
||||
// =====================================================
|
||||
$this->db->where('period', $period);
|
||||
$this->db->delete('ai_insights');
|
||||
|
||||
// =====================================================
|
||||
// 1. FINANCE - OVERDUE INVOICE
|
||||
// =====================================================
|
||||
$overdue = $this->db->query("
|
||||
SELECT COUNT(*) as total
|
||||
FROM invoices
|
||||
WHERE status != 'paid'
|
||||
AND jatuh_tempo < CURDATE()
|
||||
AND deleted_at IS NULL
|
||||
")->row()->total;
|
||||
|
||||
if ($overdue > 0) {
|
||||
$this->db->insert('ai_insights', [
|
||||
'type' => 'finance',
|
||||
'severity' => 'warning',
|
||||
'message' => "$overdue invoice sudah melewati jatuh tempo",
|
||||
'meta' => json_encode([
|
||||
'overdue_count' => (int)$overdue
|
||||
]),
|
||||
'period' => $period
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 2. FINANCE - CASHFLOW BULAN INI
|
||||
// =====================================================
|
||||
$cash = $this->db->query("
|
||||
SELECT COALESCE(SUM(jd.debit),0) as cash_in
|
||||
FROM journal_details jd
|
||||
JOIN journals j ON j.id = jd.journal_id
|
||||
WHERE MONTH(j.tanggal) = MONTH(CURDATE())
|
||||
AND YEAR(j.tanggal) = YEAR(CURDATE())
|
||||
")->row()->cash_in;
|
||||
|
||||
if ($cash > 0) {
|
||||
$this->db->insert('ai_insights', [
|
||||
'type' => 'finance',
|
||||
'severity' => ($cash > 10000000) ? 'info' : 'warning',
|
||||
'message' => "Cashflow bulan ini Rp " . number_format($cash),
|
||||
'meta' => json_encode([
|
||||
'cash_in' => (float)$cash
|
||||
]),
|
||||
'period' => $period
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 3. STOCK - KRITIS
|
||||
// =====================================================
|
||||
$stocks = $this->db->query("
|
||||
SELECT
|
||||
i.kode_id,
|
||||
k.nama_barang,
|
||||
SUM(i.stok) as total_stok,
|
||||
k.limit_stock
|
||||
FROM items i
|
||||
JOIN kode_barang k ON k.id = i.kode_id
|
||||
GROUP BY i.kode_id, k.nama_barang, k.limit_stock
|
||||
HAVING total_stok <= k.limit_stock
|
||||
")->result();
|
||||
|
||||
foreach ($stocks as $s) {
|
||||
$this->db->insert('ai_insights', [
|
||||
'type' => 'stock',
|
||||
'severity' => 'critical',
|
||||
'message' => "Stok {$s->nama_barang} kritis ({$s->total_stok})",
|
||||
'meta' => json_encode([
|
||||
'kode_id' => $s->kode_id,
|
||||
'item' => $s->nama_barang,
|
||||
'stok' => (int)$s->total_stok,
|
||||
'limit' => (int)$s->limit_stock
|
||||
]),
|
||||
'period' => $period
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 4. CUSTOMER - PAYMENT RISK
|
||||
// =====================================================
|
||||
$lateCustomers = $this->db->query("
|
||||
SELECT
|
||||
c.nama,
|
||||
COUNT(i.id) as total_invoice
|
||||
FROM invoices i
|
||||
JOIN customers c ON c.id = i.customer_id
|
||||
WHERE i.status != 'paid'
|
||||
AND i.jatuh_tempo < CURDATE()
|
||||
GROUP BY i.customer_id
|
||||
HAVING total_invoice > 2
|
||||
")->result();
|
||||
|
||||
foreach ($lateCustomers as $c) {
|
||||
$this->db->insert('ai_insights', [
|
||||
'type' => 'customer',
|
||||
'severity' => 'warning',
|
||||
'message' => "Customer {$c->nama} memiliki {$c->total_invoice} invoice overdue",
|
||||
'meta' => json_encode([
|
||||
'customer' => $c->nama,
|
||||
'overdue_invoice' => (int)$c->total_invoice
|
||||
]),
|
||||
'period' => $period
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// RESPONSE
|
||||
// =====================================================
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'AI Insight generated (REPLACE MODE)',
|
||||
'period' => $period
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Customers extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ["active_menu" => "customers"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('customers/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DATATABLE
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$this->dm->setTable('customers')
|
||||
->setColumn(
|
||||
[null,'nama','alamat','telp','email',null],
|
||||
['nama','alamat','telp','email'],
|
||||
['id'=>'DESC']
|
||||
);
|
||||
|
||||
$list = $this->dm->get_datatables();
|
||||
|
||||
$data = [];
|
||||
$no = $_POST['start'];
|
||||
|
||||
foreach ($list as $row) {
|
||||
$no++;
|
||||
|
||||
$aksi = '
|
||||
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
';
|
||||
|
||||
$data[] = [
|
||||
$no,
|
||||
$row->nama,
|
||||
$row->alamat,
|
||||
$row->telp,
|
||||
$row->email,
|
||||
$aksi
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"draw" => $_POST['draw'],
|
||||
"recordsTotal" => $this->dm->count_all(),
|
||||
"recordsFiltered" => $this->dm->count_filtered(),
|
||||
"data" => $data
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SAVE
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$data = [
|
||||
'nama' => $this->input->post('nama'),
|
||||
'alamat' => $this->input->post('alamat'),
|
||||
'telp' => $this->input->post('telp'),
|
||||
'email' => $this->input->post('email')
|
||||
];
|
||||
|
||||
if (!$data['nama']) {
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dm->setTable('customers')->insert($data);
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
log_activity(
|
||||
'customers',
|
||||
'create',
|
||||
'Menambahkan customer: '.$data['nama'],
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Data berhasil disimpan']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail($id)
|
||||
{
|
||||
$data = $this->dm->setTable('customers')->get_by_id($id);
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// UPDATE
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
|
||||
$data = [
|
||||
'nama' => $this->input->post('nama'),
|
||||
'alamat' => $this->input->post('alamat'),
|
||||
'telp' => $this->input->post('telp'),
|
||||
'email' => $this->input->post('email')
|
||||
];
|
||||
|
||||
$this->dm->setTable('customers')->update($id, $data);
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
log_activity(
|
||||
'customers',
|
||||
'update',
|
||||
'Mengubah customer ID: '.$id.' ('.$data['nama'].')',
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Data berhasil diupdate']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
$this->dm->setTable('customers')->delete($id);
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
log_activity(
|
||||
'customers',
|
||||
'delete',
|
||||
'Menghapus customer: '.($customer->nama ?? 'ID '.$id),
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Data berhasil dihapus']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Dashboard extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->library('session');
|
||||
|
||||
// Cek apakah user sudah login
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->db->select('
|
||||
accounts.id,
|
||||
accounts.kode_akun,
|
||||
accounts.nama_akun,
|
||||
accounts.tipe,
|
||||
COALESCE(SUM(journal_details.debit),0) as debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details','accounts.id = journal_details.account_id','left');
|
||||
$this->db->where('accounts.kategori','laba_rugi');
|
||||
$this->db->where('accounts.is_active',1);
|
||||
$this->db->group_by('accounts.id');
|
||||
$this->db->order_by('accounts.kode_akun','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
// 🔥 NORMALISASI SALDO BERDASARKAN POSISI
|
||||
if ($r->tipe == 'revenue') {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
$revenue += $saldo;
|
||||
}
|
||||
|
||||
if ($r->tipe == 'expense') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
$expense += $saldo;
|
||||
}
|
||||
}
|
||||
|
||||
$summary = [
|
||||
'total_revenue' => $revenue,
|
||||
'total_expense' => $expense,
|
||||
'net_profit' => $revenue - $expense
|
||||
];
|
||||
|
||||
$today = date('Y-m');
|
||||
$ai_insight = $this->db
|
||||
->select('type, severity, message, meta, created_at')
|
||||
->where('period', $today)
|
||||
->order_by("
|
||||
CASE severity
|
||||
WHEN 'critical' THEN 1
|
||||
WHEN 'warning' THEN 2
|
||||
WHEN 'info' THEN 3
|
||||
END
|
||||
", '', false)
|
||||
->order_by('created_at', 'DESC')
|
||||
->limit(20)
|
||||
->get('ai_insights')
|
||||
->result();
|
||||
|
||||
$data = [
|
||||
"active_menu" => "dashboard",
|
||||
"summary" => $summary,
|
||||
"ai_insight" => $ai_insight
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('dashboard/layout', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_activity()
|
||||
{
|
||||
$this->db->select('activity_logs.*, users.nama');
|
||||
$this->db->from('activity_logs');
|
||||
$this->db->join('users','users.id = activity_logs.user_id','left');
|
||||
|
||||
if ($this->session->userdata('role') != 'Admin') {
|
||||
$this->db->where('user_id',$this->session->userdata('user_id'));
|
||||
}
|
||||
|
||||
$this->db->order_by('created_at','DESC');
|
||||
$this->db->limit(10);
|
||||
|
||||
$data = $this->db->get()->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function log_activity()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "dashboard"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('dashboard/activity_logs', $data);
|
||||
$this->load->view('partials/footer');
|
||||
|
||||
}
|
||||
|
||||
public function get_activity_datatable()
|
||||
{
|
||||
$this->load->database();
|
||||
|
||||
$draw = $this->input->post('draw');
|
||||
$start = $this->input->post('start');
|
||||
$length = $this->input->post('length');
|
||||
$search = $this->input->post('search')['value'] ?? '';
|
||||
|
||||
// =========================
|
||||
// BASE QUERY
|
||||
// =========================
|
||||
$this->db->select('
|
||||
activity_logs.*,
|
||||
users.nama
|
||||
');
|
||||
$this->db->from('activity_logs');
|
||||
$this->db->join('users', 'users.id = activity_logs.user_id', 'left');
|
||||
|
||||
// =========================
|
||||
// SEARCH
|
||||
// =========================
|
||||
if (!empty($search)) {
|
||||
$this->db->group_start();
|
||||
$this->db->like('activity_logs.module', $search);
|
||||
$this->db->or_like('activity_logs.action', $search);
|
||||
$this->db->or_like('activity_logs.description', $search);
|
||||
$this->db->or_like('activity_logs.status', $search);
|
||||
$this->db->or_like('users.nama', $search);
|
||||
$this->db->group_end();
|
||||
}
|
||||
|
||||
// =========================
|
||||
// COUNT FILTERED
|
||||
// =========================
|
||||
$filtered_query = clone $this->db;
|
||||
$recordsFiltered = $filtered_query->count_all_results('', false);
|
||||
|
||||
// =========================
|
||||
// ORDER
|
||||
// =========================
|
||||
$this->db->order_by('activity_logs.id', 'DESC');
|
||||
|
||||
// =========================
|
||||
// LIMIT
|
||||
// =========================
|
||||
if ($length != -1) {
|
||||
$this->db->limit($length, $start);
|
||||
}
|
||||
|
||||
$query = $this->db->get();
|
||||
$data = $query->result();
|
||||
|
||||
// =========================
|
||||
// TOTAL RECORD
|
||||
// =========================
|
||||
$recordsTotal = $this->db->count_all('activity_logs');
|
||||
|
||||
// =========================
|
||||
// FORMAT OUTPUT
|
||||
// =========================
|
||||
$result = [];
|
||||
|
||||
foreach ($data as $row) {
|
||||
$result[] = [
|
||||
'created_at' => $row->created_at,
|
||||
'nama' => $row->nama ?? 'System',
|
||||
'module' => $row->module,
|
||||
'action' => $row->action,
|
||||
'description' => $row->description,
|
||||
'status' => $row->status,
|
||||
'ip_address' => $row->ip_address
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"draw" => intval($draw),
|
||||
"recordsTotal" => $recordsTotal,
|
||||
"recordsFiltered" => $recordsFiltered,
|
||||
"data" => $result
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_chart()
|
||||
{
|
||||
$year = $this->input->get('year');
|
||||
|
||||
// 🔥 DEFAULT: tahun berjalan
|
||||
if (empty($year)) {
|
||||
$year = date('Y');
|
||||
}
|
||||
|
||||
$this->db->select('
|
||||
journals.tanggal,
|
||||
accounts.tipe,
|
||||
SUM(journal_details.debit) as total_debit,
|
||||
SUM(journal_details.kredit) as total_kredit
|
||||
');
|
||||
$this->db->from('journal_details');
|
||||
$this->db->join('journals', 'journals.id = journal_details.journal_id', 'left');
|
||||
$this->db->join('accounts', 'accounts.id = journal_details.account_id', 'left');
|
||||
$this->db->where('accounts.kategori', 'laba_rugi');
|
||||
$this->db->where('accounts.is_active', 1);
|
||||
|
||||
// 🔥 pakai tahun default / filter
|
||||
$this->db->where('YEAR(journals.tanggal)', $year);
|
||||
|
||||
$this->db->group_by(['journals.tanggal', 'accounts.tipe']);
|
||||
$this->db->order_by('journals.tanggal', 'ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$map = [];
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
$date = $r->tanggal;
|
||||
|
||||
if (!isset($map[$date])) {
|
||||
$map[$date] = [
|
||||
'revenue' => 0,
|
||||
'expense' => 0
|
||||
];
|
||||
}
|
||||
|
||||
if ($r->tipe == 'revenue') {
|
||||
$map[$date]['revenue'] += ($r->total_kredit - $r->total_debit);
|
||||
}
|
||||
|
||||
if ($r->tipe == 'expense') {
|
||||
$map[$date]['expense'] += ($r->total_debit - $r->total_kredit);
|
||||
}
|
||||
}
|
||||
|
||||
$labels = [];
|
||||
$revenue = [];
|
||||
$expense = [];
|
||||
|
||||
foreach ($map as $date => $val) {
|
||||
$labels[] = $date;
|
||||
$revenue[] = (float)$val['revenue'];
|
||||
$expense[] = (float)$val['expense'];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'year' => $year, // optional biar debugging enak
|
||||
'labels' => $labels,
|
||||
'revenue' => $revenue,
|
||||
'expense' => $expense
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_years()
|
||||
{
|
||||
$this->db->select('DISTINCT YEAR(tanggal) as year');
|
||||
$this->db->from('journals');
|
||||
$this->db->order_by('year', 'DESC');
|
||||
|
||||
$data = $this->db->get()->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Employees extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
$this->load->helper(['url', 'file']);
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// INDEX
|
||||
// =========================================================
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "employees"
|
||||
];
|
||||
|
||||
$data['departments'] = $this->db
|
||||
->where('is_active',1)
|
||||
->order_by('department_name','ASC')
|
||||
->get('k_departments')
|
||||
->result();
|
||||
|
||||
$data['positions'] = $this->db
|
||||
->where('is_active',1)
|
||||
->order_by('position_name','ASC')
|
||||
->get('k_positions')
|
||||
->result();
|
||||
|
||||
$data['branches'] = $this->db
|
||||
->order_by('branch_name','ASC')
|
||||
->get('k_branches')
|
||||
->result();
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/employees', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// GET ALL
|
||||
// =========================================================
|
||||
public function getAll()
|
||||
{
|
||||
try {
|
||||
|
||||
$data = $this->db
|
||||
->select("
|
||||
e.*,
|
||||
|
||||
d.department_name,
|
||||
p.position_name,
|
||||
b.branch_name
|
||||
")
|
||||
->from('k_employees e')
|
||||
|
||||
->join(
|
||||
'k_departments d',
|
||||
'd.id = e.department_id',
|
||||
'left'
|
||||
)
|
||||
|
||||
->join(
|
||||
'k_positions p',
|
||||
'p.id = e.position_id',
|
||||
'left'
|
||||
)
|
||||
|
||||
->join(
|
||||
'k_branches b',
|
||||
'b.id = e.branch_id',
|
||||
'left'
|
||||
)
|
||||
|
||||
->order_by('e.department_id','ASC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// DETAIL
|
||||
// =========================================================
|
||||
public function detail($id = null)
|
||||
{
|
||||
try {
|
||||
|
||||
$data = $this->db
|
||||
->select("
|
||||
e.*,
|
||||
|
||||
d.department_name,
|
||||
p.position_name,
|
||||
b.branch_name
|
||||
")
|
||||
->from('k_employees e')
|
||||
|
||||
->join(
|
||||
'k_departments d',
|
||||
'd.id = e.department_id',
|
||||
'left'
|
||||
)
|
||||
|
||||
->join(
|
||||
'k_positions p',
|
||||
'p.id = e.position_id',
|
||||
'left'
|
||||
)
|
||||
|
||||
->join(
|
||||
'k_branches b',
|
||||
'b.id = e.branch_id',
|
||||
'left'
|
||||
)
|
||||
|
||||
->where('e.id',$id)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
if (!$data) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => 'Data tidak ditemukan'
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// STORE
|
||||
// =========================================================
|
||||
public function store()
|
||||
{
|
||||
try {
|
||||
|
||||
if (empty(trim($this->input->post('full_name')))) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message'=> 'Nama wajib diisi'
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($this->input->post('join_date'))) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message'=> 'Tanggal masuk wajib diisi'
|
||||
]);
|
||||
}
|
||||
|
||||
$employee_code = $this->generateEmployeeCode();
|
||||
|
||||
$photo = $this->uploadPhoto();
|
||||
|
||||
$data = $this->prepareData();
|
||||
|
||||
$data['employee_code'] = $employee_code;
|
||||
$data['photo'] = $photo;
|
||||
|
||||
$this->db->insert('k_employees',$data);
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'message' => 'Berhasil simpan data'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// UPDATE
|
||||
// =========================================================
|
||||
public function update($id = null)
|
||||
{
|
||||
try {
|
||||
|
||||
$employee = $this->db
|
||||
->get_where('k_employees',['id'=>$id])
|
||||
->row();
|
||||
|
||||
if (!$employee) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => 'Data tidak ditemukan'
|
||||
]);
|
||||
}
|
||||
|
||||
$data = $this->prepareData();
|
||||
|
||||
if (!empty($_FILES['photo_file']['name'])) {
|
||||
|
||||
$photo = $this->uploadPhoto();
|
||||
|
||||
if (
|
||||
!empty($employee->photo)
|
||||
&&
|
||||
file_exists(FCPATH . $employee->photo)
|
||||
) {
|
||||
unlink(FCPATH . $employee->photo);
|
||||
}
|
||||
|
||||
$data['photo'] = $photo;
|
||||
}
|
||||
|
||||
$this->db
|
||||
->where('id',$id)
|
||||
->update('k_employees',$data);
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'message' => 'Berhasil update data'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// DELETE
|
||||
// =========================================================
|
||||
public function delete($id = null)
|
||||
{
|
||||
try {
|
||||
|
||||
$employee = $this->db
|
||||
->get_where('k_employees',['id'=>$id])
|
||||
->row();
|
||||
|
||||
if (!$employee) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => 'Data tidak ditemukan'
|
||||
]);
|
||||
}
|
||||
|
||||
if (
|
||||
!empty($employee->photo)
|
||||
&&
|
||||
file_exists(FCPATH . $employee->photo)
|
||||
) {
|
||||
unlink(FCPATH . $employee->photo);
|
||||
}
|
||||
|
||||
$this->db->delete('k_employees',[
|
||||
'id'=>$id
|
||||
]);
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'message' => 'Berhasil hapus data'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
return $this->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// GENERATE CODE
|
||||
// =========================================================
|
||||
private function generateEmployeeCode()
|
||||
{
|
||||
$last = $this->db
|
||||
->order_by('id','DESC')
|
||||
->get('k_employees')
|
||||
->row();
|
||||
|
||||
$number = $last ? $last->id + 1 : 1;
|
||||
|
||||
return 'RJN-' . str_pad($number,5,'0',STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// UPLOAD PHOTO
|
||||
// =========================================================
|
||||
private function uploadPhoto()
|
||||
{
|
||||
if (empty($_FILES['photo_file']['name'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = FCPATH . 'uploads/karyawan/';
|
||||
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path,0777,true);
|
||||
}
|
||||
|
||||
$config['upload_path'] = './uploads/karyawan/';
|
||||
$config['allowed_types'] = 'jpg|jpeg|png|webp';
|
||||
$config['max_size'] = 2048;
|
||||
$config['encrypt_name'] = true;
|
||||
|
||||
$this->load->library('upload',$config);
|
||||
|
||||
if (!$this->upload->do_upload('photo_file')) {
|
||||
|
||||
throw new Exception(
|
||||
strip_tags(
|
||||
$this->upload->display_errors()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$upload = $this->upload->data();
|
||||
|
||||
return 'uploads/karyawan/' . $upload['file_name'];
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// PREPARE DATA
|
||||
// =========================================================
|
||||
private function prepareData()
|
||||
{
|
||||
return [
|
||||
|
||||
'nik' => trim($this->input->post('nik')),
|
||||
|
||||
'full_name' => trim(
|
||||
$this->input->post('full_name')
|
||||
),
|
||||
|
||||
'gender' => $this->input->post('gender'),
|
||||
|
||||
'birth_place' => trim(
|
||||
$this->input->post('birth_place')
|
||||
),
|
||||
|
||||
'birth_date' => $this->input->post('birth_date'),
|
||||
|
||||
'phone' => trim(
|
||||
$this->input->post('phone')
|
||||
),
|
||||
|
||||
'email' => trim(
|
||||
$this->input->post('email')
|
||||
),
|
||||
|
||||
'address' => trim(
|
||||
$this->input->post('address')
|
||||
),
|
||||
|
||||
'join_date' => $this->input->post('join_date'),
|
||||
|
||||
'resign_date' => $this->input->post('resign_date'),
|
||||
|
||||
'employment_status' => $this->input->post('employment_status'),
|
||||
|
||||
'marital_status' => $this->input->post('marital_status'),
|
||||
|
||||
'religion' => trim(
|
||||
$this->input->post('religion')
|
||||
),
|
||||
|
||||
'department_id' => $this->input->post('department_id'),
|
||||
|
||||
'position_id' => $this->input->post('position_id'),
|
||||
|
||||
'branch_id' => $this->input->post('branch_id'),
|
||||
|
||||
'basic_salary' => $this->input->post('basic_salary') ?: 0,
|
||||
|
||||
'bank_name' => trim(
|
||||
$this->input->post('bank_name')
|
||||
),
|
||||
|
||||
'bank_account_number' => trim(
|
||||
$this->input->post('bank_account_number')
|
||||
),
|
||||
|
||||
'bank_account_name' => trim(
|
||||
$this->input->post('bank_account_name')
|
||||
),
|
||||
|
||||
'bpjs_kesehatan_number' => trim(
|
||||
$this->input->post('bpjs_kesehatan_number')
|
||||
),
|
||||
|
||||
'bpjs_ketenagakerjaan_number' => trim(
|
||||
$this->input->post('bpjs_ketenagakerjaan_number')
|
||||
),
|
||||
|
||||
'fingerprint_user_id' => trim(
|
||||
$this->input->post('fingerprint_user_id')
|
||||
),
|
||||
|
||||
'is_active' => $this->input->post('is_active') ?: 1
|
||||
];
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// JSON
|
||||
// =========================================================
|
||||
private function json($data)
|
||||
{
|
||||
return $this->output
|
||||
->set_content_type('application/json')
|
||||
->set_output(json_encode($data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generateinvoice extends CI_Controller {
|
||||
|
||||
public function generate($invoice_id)
|
||||
{
|
||||
// ❗ WAJIB: hindari output lain sebelum PDF
|
||||
ob_end_clean();
|
||||
|
||||
// =========================
|
||||
// 🔥 AMBIL DATA PERUSAHAAN
|
||||
// =========================
|
||||
$invp = $this->db->get('pengaturan')->row_array(); // 🔥 FIX: tidak pakai GET server_id
|
||||
|
||||
if(!$invp){
|
||||
show_error('Data perusahaan tidak ditemukan');
|
||||
}
|
||||
|
||||
$nama = $invp['nama_perusahaan'];
|
||||
$alamat = $invp['alamat_perusahaan'];
|
||||
$telepon = $invp['telepon_perusahaan'];
|
||||
$Keterangan = $invp['catatan_perusahaan'];
|
||||
$Pembuat = $invp['nama_bendahara'];
|
||||
$logo = base_url('uploads/img/logo-ljn.png');
|
||||
|
||||
// =========================
|
||||
// 🔥 GET INVOICE + CUSTOMER
|
||||
// =========================
|
||||
$invi = $this->db->select('invoices.*, customers.nama, customers.alamat, customers.telp')
|
||||
->from('invoices')
|
||||
->join('customers', 'customers.id = invoices.customer_id', 'left')
|
||||
->where('invoices.id', $invoice_id)
|
||||
->get()
|
||||
->row_array();
|
||||
|
||||
// =========================
|
||||
// VALIDASI
|
||||
// =========================
|
||||
if(!$invi){
|
||||
show_error('Invoice tidak ditemukan');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// MAPPING DATA
|
||||
// =========================
|
||||
$customerAddress = $invi['alamat'] ?? '-';
|
||||
$customerName = $invi['nama'] ?? '-';
|
||||
$customerNumber = $invi['telp'] ?? '-';
|
||||
|
||||
$invoiceNumber = $invi['no_invoice'] ?? '-';
|
||||
$invoiceDate = $invi['tanggal'] ?? '-'; // 🔥 sesuaikan field kamu
|
||||
$invoiceLimit = $invi['jatuh_tempo'] ?? '-'; // 🔥 sesuaikan juga
|
||||
|
||||
// =========================
|
||||
// 🔥 DETAIL ITEM
|
||||
// =========================
|
||||
$header = ['No','Tanggal','Item','Deskripsi','Qty','Harga', 'Subtotal'];
|
||||
$data = [];
|
||||
|
||||
// 🔥 NOTE: sesuaikan tabel kamu (invoice_details / invoice_list)
|
||||
// $detail = $this->db->get_where('invoice_details', [
|
||||
// 'invoice_id' => $invoice_id
|
||||
// ])->result_array();
|
||||
|
||||
$detail = $this->db
|
||||
->where('invoice_id', $invoice_id)
|
||||
->order_by('tanggal', 'ASC')
|
||||
->get('invoice_details')
|
||||
->result_array();
|
||||
|
||||
$no = 1;
|
||||
foreach($detail as $d){
|
||||
$data[] = [
|
||||
$no++,
|
||||
$d['tanggal'],
|
||||
$d['nama_item'],
|
||||
$d['keterangan'],
|
||||
$d['qty'],
|
||||
number_format($d['harga']),
|
||||
number_format($d['subtotal'])
|
||||
];
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 🔥 TOTAL
|
||||
// =========================
|
||||
$total = (float)$invi['total'];
|
||||
|
||||
$this->db->select_sum('sisa_piutang');
|
||||
$this->db->where('customer_id', $invi['customer_id']);
|
||||
$this->db->where('status !=', 'paid');
|
||||
$this->db->where('id !=', $invoice_id); // exclude invoice sekarang
|
||||
|
||||
$tunggakan = $this->db->get('invoices')->row()->sisa_piutang ?? 0;
|
||||
|
||||
// Ngambil Data Tunggakan bulan lalu jika ada.
|
||||
$keteranganlain = 'Akumulasi Tunggakan Invoice Bulan Lalu';
|
||||
$nom_lainnya = $tunggakan;
|
||||
|
||||
$subtotal = number_format($total);
|
||||
$lainnya = number_format($nom_lainnya);
|
||||
$totalAmount = number_format($total + $nom_lainnya);
|
||||
$terbilang = trim($this->terbilang($total + $nom_lainnya)).' rupiah';
|
||||
|
||||
$filename = 'INV-'.$customerName.'-'.$invoiceNumber.'.pdf';
|
||||
|
||||
// =========================
|
||||
// 🔥 LOAD FPDF
|
||||
// =========================
|
||||
require_once(APPPATH.'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH.'libraries/PDF_Invoice.php');
|
||||
|
||||
$pdf = new PDF();
|
||||
|
||||
// 🔥 FIX PATH LOGO (WAJIB)
|
||||
$logo_path = FCPATH.'img/user/'.$logo;
|
||||
if(!file_exists($logo_path)){
|
||||
$logo_path = ''; // biar gak error kalau logo tidak ada
|
||||
}
|
||||
|
||||
$pdf->setHeaderData($nama, $alamat, $telepon, $logo);
|
||||
$pdf->AddPage();
|
||||
$pdf->SetFont('Arial','',12);
|
||||
|
||||
$pdf->CustomerDetails(
|
||||
$customerName,
|
||||
$customerAddress,
|
||||
$invoiceNumber,
|
||||
$invoiceDate,
|
||||
$customerNumber,
|
||||
$invoiceLimit
|
||||
);
|
||||
|
||||
$pdf->InvoiceTable($header, $data);
|
||||
$pdf->Total($subtotal,$lainnya,$totalAmount,$terbilang,$keteranganlain);
|
||||
$pdf->Keterangan($Keterangan);
|
||||
$pdf->Pembuat($Pembuat);
|
||||
|
||||
// =========================
|
||||
// 🔥 OUTPUT LANGSUNG (TANPA SIMPAN)
|
||||
// =========================
|
||||
$pdf->Output('I', $filename);
|
||||
|
||||
|
||||
// =========================
|
||||
// LOG ACTIVITY
|
||||
// =========================
|
||||
log_activity(
|
||||
'invoice',
|
||||
'generate',
|
||||
'Generate invoice #' . $invoiceNumber . ' untuk ' . $customerName,
|
||||
'success'
|
||||
);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 🔥 TERBILANG
|
||||
// =========================
|
||||
private function terbilang($angka)
|
||||
{
|
||||
$angka = abs($angka);
|
||||
$baca = ["", "satu", "dua", "tiga", "empat", "lima", "enam", "tujuh", "delapan", "sembilan", "sepuluh", "sebelas"];
|
||||
|
||||
if ($angka < 12) {
|
||||
return " " . $baca[$angka];
|
||||
} elseif ($angka < 20) {
|
||||
return $this->terbilang($angka - 10) . " belas";
|
||||
} elseif ($angka < 100) {
|
||||
return $this->terbilang(intval($angka / 10)) . " puluh" . $this->terbilang($angka % 10);
|
||||
} elseif ($angka < 200) {
|
||||
return " seratus" . $this->terbilang($angka - 100);
|
||||
} elseif ($angka < 1000) {
|
||||
return $this->terbilang(intval($angka / 100)) . " ratus" . $this->terbilang($angka % 100);
|
||||
} elseif ($angka < 2000) {
|
||||
return " seribu" . $this->terbilang($angka - 1000);
|
||||
} elseif ($angka < 1000000) {
|
||||
return $this->terbilang(intval($angka / 1000)) . " ribu" . $this->terbilang($angka % 1000);
|
||||
} elseif ($angka < 1000000000) {
|
||||
return $this->terbilang(intval($angka / 1000000)) . " juta" . $this->terbilang($angka % 1000000);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,768 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generateschedule extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// INDEX
|
||||
// =====================================================
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "generate_schedule"
|
||||
];
|
||||
|
||||
// ============================================
|
||||
// POSITIONS
|
||||
// ============================================
|
||||
$data['positions'] = $this->db
|
||||
->where('is_active',1)
|
||||
->order_by('position_name','ASC')
|
||||
->get('k_positions')
|
||||
->result();
|
||||
|
||||
// ============================================
|
||||
// SHIFTS
|
||||
// ============================================
|
||||
$data['shifts'] = $this->db
|
||||
->order_by('shift_code','ASC')
|
||||
->get('k_shifts')
|
||||
->result();
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/generate_schedule', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// CALENDAR DATA
|
||||
// =====================================================
|
||||
public function calendar_data()
|
||||
{
|
||||
$month = (int)$this->input->get('month');
|
||||
$year = (int)$this->input->get('year');
|
||||
|
||||
if(empty($month)){
|
||||
$month = date('n');
|
||||
}
|
||||
|
||||
if(empty($year)){
|
||||
$year = date('Y');
|
||||
}
|
||||
|
||||
$firstDate = $year . '-'
|
||||
. str_pad($month,2,'0',STR_PAD_LEFT)
|
||||
. '-01';
|
||||
|
||||
// ============================================
|
||||
// START MONDAY
|
||||
// ============================================
|
||||
$startDate = date(
|
||||
'Y-m-d',
|
||||
strtotime('monday this week', strtotime($firstDate))
|
||||
);
|
||||
|
||||
if(date('N', strtotime($firstDate)) == 1){
|
||||
$startDate = $firstDate;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// END SUNDAY
|
||||
// ============================================
|
||||
$lastDate = date(
|
||||
'Y-m-t',
|
||||
strtotime($firstDate)
|
||||
);
|
||||
|
||||
$endDate = date(
|
||||
'Y-m-d',
|
||||
strtotime('sunday this week', strtotime($lastDate))
|
||||
);
|
||||
|
||||
$days = [];
|
||||
|
||||
$current = strtotime($startDate);
|
||||
$end = strtotime($endDate);
|
||||
|
||||
while($current <= $end){
|
||||
|
||||
$date = date('Y-m-d', $current);
|
||||
|
||||
// ========================================
|
||||
// GET SCHEDULE GROUP BY SHIFT
|
||||
// ========================================
|
||||
$this->db->select('
|
||||
sh.id as shift_id,
|
||||
sh.shift_name,
|
||||
|
||||
p.position_name,
|
||||
|
||||
COUNT(s.id) as total_employee
|
||||
');
|
||||
|
||||
$this->db->from('k_employee_shift_schedules s');
|
||||
|
||||
$this->db->join(
|
||||
'k_employees e',
|
||||
'e.id = s.employee_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->join(
|
||||
'k_positions p',
|
||||
'p.id = e.position_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->join(
|
||||
'k_shifts sh',
|
||||
'sh.id = s.shift_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->where('DATE(s.start_datetime)', $date);
|
||||
|
||||
// ========================================
|
||||
// GROUPING
|
||||
// ========================================
|
||||
$this->db->group_by('sh.id');
|
||||
$this->db->group_by('p.id');
|
||||
|
||||
$this->db->order_by('sh.checkin_time','ASC');
|
||||
$this->db->order_by('p.position_name','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
// ========================================
|
||||
// FORMAT GROUPED
|
||||
// ========================================
|
||||
$grouped = [];
|
||||
|
||||
if($rows){
|
||||
|
||||
foreach($rows as $r){
|
||||
|
||||
$shiftId = $r->shift_id ?: 0;
|
||||
|
||||
if(!isset($grouped[$shiftId])){
|
||||
|
||||
$grouped[$shiftId] = [
|
||||
|
||||
'shift_id' => $r->shift_id,
|
||||
|
||||
'shift_name' => $r->shift_name ?: '-',
|
||||
|
||||
'positions' => []
|
||||
];
|
||||
}
|
||||
|
||||
$grouped[$shiftId]['positions'][] = [
|
||||
|
||||
'position_name' => $r->position_name ?: '-',
|
||||
|
||||
'total' => (int)$r->total_employee
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$days[] = [
|
||||
|
||||
'date' => date('d', strtotime($date)),
|
||||
|
||||
'month' => date('M', strtotime($date)),
|
||||
|
||||
'day_name' => $this->translateDay(
|
||||
date('l', strtotime($date))
|
||||
),
|
||||
|
||||
'full_date' => tanggal_indo($date),
|
||||
|
||||
'full_date_raw' => $date,
|
||||
|
||||
'is_current_month' => (
|
||||
date('m', strtotime($date))
|
||||
==
|
||||
str_pad($month,2,'0',STR_PAD_LEFT)
|
||||
),
|
||||
|
||||
'shifts' => array_values($grouped)
|
||||
];
|
||||
|
||||
$current = strtotime('+1 day', $current);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'days' => $days
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// GET EMPLOYEE
|
||||
// =====================================================
|
||||
public function get_employees()
|
||||
{
|
||||
$position_id = $this->input->get('position_id');
|
||||
|
||||
$this->db->select('
|
||||
e.id,
|
||||
e.full_name,
|
||||
e.fingerprint_user_id,
|
||||
|
||||
p.position_name
|
||||
');
|
||||
|
||||
$this->db->from('k_employees e');
|
||||
|
||||
$this->db->join(
|
||||
'k_positions p',
|
||||
'p.id=e.position_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->where('e.is_active',1);
|
||||
|
||||
if(!empty($position_id)){
|
||||
$this->db->where('e.position_id',$position_id);
|
||||
}
|
||||
|
||||
$this->db->order_by('e.full_name','ASC');
|
||||
|
||||
$employees = $this->db->get()->result();
|
||||
|
||||
echo json_encode([
|
||||
'data' => $employees
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// SAVE MANUAL SCHEDULE
|
||||
// =====================================================
|
||||
public function save_manual_schedule()
|
||||
{
|
||||
try{
|
||||
|
||||
$employee_ids = $this->input->post('employee_ids');
|
||||
|
||||
$schedule_date = $this->input->post('schedule_date');
|
||||
|
||||
$shift_id = $this->input->post('shift_id');
|
||||
|
||||
// ============================================
|
||||
// VALIDASI
|
||||
// ============================================
|
||||
if(empty($employee_ids)){
|
||||
throw new Exception('Employee belum dipilih');
|
||||
}
|
||||
|
||||
if(empty($schedule_date)){
|
||||
throw new Exception('Tanggal wajib diisi');
|
||||
}
|
||||
|
||||
if(empty($shift_id)){
|
||||
throw new Exception('Shift wajib dipilih');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// GET SHIFT
|
||||
// ============================================
|
||||
$shift = $this->db
|
||||
->where('id', $shift_id)
|
||||
->get('k_shifts')
|
||||
->row();
|
||||
|
||||
if(!$shift){
|
||||
throw new Exception('Shift tidak ditemukan');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BUILD START DATETIME
|
||||
// ============================================
|
||||
$start_datetime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime(
|
||||
$schedule_date . ' ' . $shift->checkin_time
|
||||
)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// BUILD END DATETIME
|
||||
// ============================================
|
||||
$end_datetime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime(
|
||||
$schedule_date . ' ' . $shift->checkout_time
|
||||
)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// JIKA SHIFT NYEBRANG HARI
|
||||
// ============================================
|
||||
$is_cross_day = 0;
|
||||
|
||||
if(
|
||||
strtotime($shift->checkout_time)
|
||||
<=
|
||||
strtotime($shift->checkin_time)
|
||||
){
|
||||
|
||||
$end_datetime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime('+1 day', strtotime($end_datetime))
|
||||
);
|
||||
|
||||
$is_cross_day = 1;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// HITUNG TOTAL JAM KERJA
|
||||
// ============================================
|
||||
$work_hours = (
|
||||
strtotime($end_datetime)
|
||||
-
|
||||
strtotime($start_datetime)
|
||||
) / 3600;
|
||||
|
||||
// ============================================
|
||||
// LOOP EMPLOYEE
|
||||
// ============================================
|
||||
foreach($employee_ids as $employee_id){
|
||||
|
||||
// ========================================
|
||||
// CHECK EXIST
|
||||
// ========================================
|
||||
$exists = $this->db
|
||||
->where('employee_id', $employee_id)
|
||||
->where('schedule_date', $schedule_date)
|
||||
->get('k_employee_shift_schedules')
|
||||
->row();
|
||||
|
||||
// ========================================
|
||||
// DATA SAVE
|
||||
// ========================================
|
||||
$saveData = [
|
||||
|
||||
'employee_id' => $employee_id,
|
||||
|
||||
'shift_id' => $shift_id,
|
||||
|
||||
'start_datetime' => $start_datetime,
|
||||
|
||||
'end_datetime' => $end_datetime,
|
||||
|
||||
'work_hours' => $work_hours,
|
||||
|
||||
'is_cross_day' => $is_cross_day,
|
||||
|
||||
'schedule_source' => 'manual',
|
||||
|
||||
'notes' => 'manual planner',
|
||||
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// ========================================
|
||||
// UPDATE
|
||||
// ========================================
|
||||
if($exists){
|
||||
|
||||
$this->db
|
||||
->where('id', $exists->id)
|
||||
->update(
|
||||
'k_employee_shift_schedules',
|
||||
$saveData
|
||||
);
|
||||
|
||||
// ========================================
|
||||
// INSERT
|
||||
// ========================================
|
||||
} else {
|
||||
|
||||
$saveData['created_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
$this->db->insert(
|
||||
'k_employee_shift_schedules',
|
||||
$saveData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Jadwal berhasil disimpan'
|
||||
]);
|
||||
|
||||
} catch(Exception $e){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// TRANSLATE DAY
|
||||
// =====================================================
|
||||
private function translateDay($day)
|
||||
{
|
||||
$days = [
|
||||
'Sunday' => 'Minggu',
|
||||
'Monday' => 'Senin',
|
||||
'Tuesday' => 'Selasa',
|
||||
'Wednesday' => 'Rabu',
|
||||
'Thursday' => 'Kamis',
|
||||
'Friday' => 'Jumat',
|
||||
'Saturday' => 'Sabtu'
|
||||
];
|
||||
|
||||
return isset($days[$day])
|
||||
? $days[$day]
|
||||
: $day;
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// DETAIL SCHEDULE
|
||||
// =====================================================
|
||||
public function detail_schedule()
|
||||
{
|
||||
$schedule_date = $this->input->get('schedule_date');
|
||||
|
||||
if(empty($schedule_date)){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message'=> 'Tanggal wajib diisi',
|
||||
'data' => []
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// GET DATA
|
||||
// ============================================
|
||||
$this->db->select('
|
||||
s.id as schedule_id,
|
||||
|
||||
s.employee_id,
|
||||
s.shift_id,
|
||||
|
||||
s.start_datetime,
|
||||
s.end_datetime,
|
||||
|
||||
e.employee_code,
|
||||
e.full_name,
|
||||
e.fingerprint_user_id,
|
||||
|
||||
p.id as position_id,
|
||||
p.position_name,
|
||||
|
||||
sh.shift_name,
|
||||
sh.checkin_time,
|
||||
sh.checkout_time,
|
||||
sh.color
|
||||
');
|
||||
|
||||
$this->db->from('k_employee_shift_schedules s');
|
||||
|
||||
$this->db->join(
|
||||
'k_employees e',
|
||||
'e.id=s.employee_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->join(
|
||||
'k_positions p',
|
||||
'p.id=e.position_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->join(
|
||||
'k_shifts sh',
|
||||
'sh.id=s.shift_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// FILTER DATE
|
||||
// ============================================
|
||||
$this->db->where(
|
||||
'DATE(s.start_datetime)',
|
||||
$schedule_date
|
||||
);
|
||||
|
||||
$this->db->order_by('p.position_name','ASC');
|
||||
|
||||
$this->db->order_by('e.full_name','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
// ============================================
|
||||
// GROUP POSITION
|
||||
// ============================================
|
||||
$grouped = [];
|
||||
|
||||
if($rows){
|
||||
|
||||
foreach($rows as $r){
|
||||
|
||||
$position_id = $r->position_id ?: 0;
|
||||
|
||||
// ====================================
|
||||
// CREATE GROUP
|
||||
// ====================================
|
||||
if(!isset($grouped[$position_id])){
|
||||
|
||||
$grouped[$position_id] = [
|
||||
|
||||
'position_id' => $r->position_id,
|
||||
|
||||
'position_name' => $r->position_name ?: '-',
|
||||
|
||||
'employees' => []
|
||||
];
|
||||
}
|
||||
|
||||
// ====================================
|
||||
// EMPLOYEE
|
||||
// ====================================
|
||||
$grouped[$position_id]['employees'][] = [
|
||||
|
||||
'schedule_id' => $r->schedule_id,
|
||||
|
||||
'employee_id' => $r->employee_id,
|
||||
|
||||
'full_name' => $r->full_name,
|
||||
|
||||
'employee_code' => $r->employee_code,
|
||||
|
||||
'fingerprint_user_id' => $r->fingerprint_user_id,
|
||||
|
||||
'shift_id' => $r->shift_id,
|
||||
|
||||
'shift_name' => $r->shift_name,
|
||||
|
||||
'checkin' => $r->checkin_time
|
||||
? date(
|
||||
'H:i',
|
||||
strtotime($r->checkin_time)
|
||||
)
|
||||
: '-',
|
||||
|
||||
'checkout' => $r->checkout_time
|
||||
? date(
|
||||
'H:i',
|
||||
strtotime($r->checkout_time)
|
||||
)
|
||||
: '-',
|
||||
|
||||
'start_datetime' => $r->start_datetime,
|
||||
|
||||
'end_datetime' => $r->end_datetime,
|
||||
|
||||
'color' => $r->color
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'data' => array_values($grouped)
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// UPDATE SCHEDULE SHIFT
|
||||
// =====================================================
|
||||
public function update_schedule_shift()
|
||||
{
|
||||
try{
|
||||
|
||||
$schedule_id = $this->input->post('schedule_id');
|
||||
|
||||
$shift_id = $this->input->post('shift_id');
|
||||
|
||||
// ============================================
|
||||
// VALIDASI
|
||||
// ============================================
|
||||
if(empty($schedule_id)){
|
||||
throw new Exception('Schedule ID kosong');
|
||||
}
|
||||
|
||||
if(empty($shift_id)){
|
||||
throw new Exception('Shift wajib dipilih');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CHECK SCHEDULE
|
||||
// ============================================
|
||||
$schedule = $this->db
|
||||
->where('id', $schedule_id)
|
||||
->get('k_employee_shift_schedules')
|
||||
->row();
|
||||
|
||||
if(!$schedule){
|
||||
throw new Exception('Schedule tidak ditemukan');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CHECK SHIFT
|
||||
// ============================================
|
||||
$shift = $this->db
|
||||
->where('id', $shift_id)
|
||||
->get('k_shifts')
|
||||
->row();
|
||||
|
||||
if(!$shift){
|
||||
throw new Exception('Shift tidak ditemukan');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// AMBIL TANGGAL
|
||||
// ============================================
|
||||
$schedule_date = date(
|
||||
'Y-m-d',
|
||||
strtotime($schedule->start_datetime)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// BUILD START DATETIME
|
||||
// ============================================
|
||||
$start_datetime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime(
|
||||
$schedule_date . ' ' . $shift->checkin_time
|
||||
)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// BUILD END DATETIME
|
||||
// ============================================
|
||||
$end_datetime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime(
|
||||
$schedule_date . ' ' . $shift->checkout_time
|
||||
)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// CROSS DAY
|
||||
// ============================================
|
||||
$is_cross_day = 0;
|
||||
|
||||
if(
|
||||
strtotime($shift->checkout_time)
|
||||
<=
|
||||
strtotime($shift->checkin_time)
|
||||
){
|
||||
|
||||
$end_datetime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime('+1 day', strtotime($end_datetime))
|
||||
);
|
||||
|
||||
$is_cross_day = 1;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// WORK HOURS
|
||||
// ============================================
|
||||
$work_hours = $shift->work_hours;
|
||||
|
||||
// ============================================
|
||||
// UPDATE
|
||||
// ============================================
|
||||
$this->db
|
||||
->where('id', $schedule_id)
|
||||
->update(
|
||||
'k_employee_shift_schedules',
|
||||
[
|
||||
|
||||
'shift_id' => $shift_id,
|
||||
|
||||
'start_datetime' => $start_datetime,
|
||||
|
||||
'end_datetime' => $end_datetime,
|
||||
|
||||
'work_hours' => $work_hours,
|
||||
|
||||
'is_cross_day' => $is_cross_day,
|
||||
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
]
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Shift berhasil diupdate'
|
||||
]);
|
||||
|
||||
} catch(Exception $e){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =====================================================
|
||||
// DELETE SCHEDULE
|
||||
// =====================================================
|
||||
public function delete_schedule()
|
||||
{
|
||||
try{
|
||||
|
||||
$schedule_id = $this->input->post('schedule_id');
|
||||
|
||||
// ============================================
|
||||
// VALIDASI
|
||||
// ============================================
|
||||
if(empty($schedule_id)){
|
||||
throw new Exception('Schedule ID kosong');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CHECK DATA
|
||||
// ============================================
|
||||
$schedule = $this->db
|
||||
->where('id', $schedule_id)
|
||||
->get('k_employee_shift_schedules')
|
||||
->row();
|
||||
|
||||
if(!$schedule){
|
||||
throw new Exception('Schedule tidak ditemukan');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// DELETE
|
||||
// ============================================
|
||||
$this->db
|
||||
->where('id', $schedule_id)
|
||||
->delete('k_employee_shift_schedules');
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Schedule berhasil dihapus'
|
||||
]);
|
||||
|
||||
} catch(Exception $e){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Holidays extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// VIEW
|
||||
// =========================================
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "holidays"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/holidays');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// GET DATA
|
||||
// =========================================
|
||||
public function get_data()
|
||||
{
|
||||
$data = $this->db
|
||||
->order_by('holiday_date','DESC')
|
||||
->get('k_holidays')
|
||||
->result();
|
||||
|
||||
$rows = [];
|
||||
|
||||
$no = 1;
|
||||
|
||||
foreach($data as $d){
|
||||
|
||||
$rows[] = [
|
||||
$no++,
|
||||
date('d M Y', strtotime($d->holiday_date)),
|
||||
$d->holiday_name,
|
||||
$d->is_national == 1
|
||||
? '<span class="badge bg-success">Nasional</span>'
|
||||
: '<span class="badge bg-secondary">Perusahaan</span>',
|
||||
'
|
||||
<button class="btn btn-warning btn-sm btn-edit"
|
||||
data-id="'.$d->id.'">
|
||||
Edit
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger btn-sm btn-delete"
|
||||
data-id="'.$d->id.'">
|
||||
Delete
|
||||
</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'data' => $rows
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// DETAIL
|
||||
// =========================================
|
||||
public function detail($id)
|
||||
{
|
||||
$data = $this->db
|
||||
->get_where('k_holidays',['id'=>$id])
|
||||
->row();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// SAVE
|
||||
// =========================================
|
||||
public function save()
|
||||
{
|
||||
$holiday_date = $this->input->post('holiday_date');
|
||||
$holiday_name = $this->input->post('holiday_name');
|
||||
$is_national = $this->input->post('is_national');
|
||||
|
||||
$cek = $this->db
|
||||
->get_where('k_holidays',[
|
||||
'holiday_date' => $holiday_date
|
||||
])
|
||||
->row();
|
||||
|
||||
if($cek){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Tanggal holiday sudah ada'
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$insert = [
|
||||
'holiday_date' => $holiday_date,
|
||||
'holiday_name' => $holiday_name,
|
||||
'is_national' => $is_national
|
||||
];
|
||||
|
||||
$this->db->insert('k_holidays',$insert);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Holiday berhasil ditambahkan'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// UPDATE
|
||||
// =========================================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$holiday_date = $this->input->post('holiday_date');
|
||||
$holiday_name = $this->input->post('holiday_name');
|
||||
$is_national = $this->input->post('is_national');
|
||||
|
||||
$cek = $this->db
|
||||
->where('holiday_date',$holiday_date)
|
||||
->where('id !=',$id)
|
||||
->get('k_holidays')
|
||||
->row();
|
||||
|
||||
if($cek){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Tanggal holiday sudah ada'
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$update = [
|
||||
'holiday_date' => $holiday_date,
|
||||
'holiday_name' => $holiday_name,
|
||||
'is_national' => $is_national
|
||||
];
|
||||
|
||||
$this->db
|
||||
->where('id',$id)
|
||||
->update('k_holidays',$update);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Holiday berhasil diupdate'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// DELETE
|
||||
// =========================================
|
||||
public function delete($id)
|
||||
{
|
||||
$this->db
|
||||
->where('id',$id)
|
||||
->delete('k_holidays');
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Holiday berhasil dihapus'
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,795 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Items extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ["active_menu" => "items"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('items/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function draft_items()
|
||||
{
|
||||
$data = ["active_menu" => "draft_items"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('items/draft_items');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA (FIX)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$status = $this->input->get('s') ?? 'all';
|
||||
|
||||
$allowed = ['draft','active','all'];
|
||||
if (!in_array($status, $allowed)) {
|
||||
$status = 'all';
|
||||
}
|
||||
|
||||
$this->db->select('
|
||||
items.id,
|
||||
items.nama_barang,
|
||||
items.kode_detail,
|
||||
items.harga_beli,
|
||||
items.harga_jual,
|
||||
items.tanggal_pembelian,
|
||||
items.created_at,
|
||||
items.stok,
|
||||
items.status,
|
||||
items.kode_id,
|
||||
(
|
||||
SELECT GROUP_CONCAT(DISTINCT w.nama SEPARATOR ", ")
|
||||
FROM stock_logs sl
|
||||
LEFT JOIN warehouses w ON sl.warehouse_id = w.id
|
||||
WHERE sl.item_id = items.id
|
||||
) as gudang
|
||||
');
|
||||
|
||||
if ($status !== 'all') {
|
||||
$this->db->where('items.status', $status);
|
||||
}
|
||||
|
||||
// urutan custom
|
||||
$this->db->order_by('(items.stok = 0)', 'ASC', false);
|
||||
$this->db->order_by('items.tanggal_pembelian', 'ASC');
|
||||
|
||||
$this->db->from('items');
|
||||
|
||||
$data = $this->db->get()->result();
|
||||
|
||||
// $this->db->select('
|
||||
// items.id,
|
||||
// items.nama_barang,
|
||||
// items.kode_detail,
|
||||
// items.harga_beli,
|
||||
// items.harga_jual,
|
||||
// items.tanggal_pembelian,
|
||||
// items.created_at,
|
||||
// items.stok,
|
||||
// items.status,
|
||||
// items.kode_id,
|
||||
// (
|
||||
// SELECT GROUP_CONCAT(DISTINCT w.nama SEPARATOR ", ")
|
||||
// FROM stock_logs sl
|
||||
// LEFT JOIN warehouses w ON sl.warehouse_id = w.id
|
||||
// WHERE sl.item_id = items.id
|
||||
// ) as gudang
|
||||
// ');
|
||||
|
||||
// if ($status !== 'all') {
|
||||
// $this->db->where('items.status', $status);
|
||||
// }
|
||||
|
||||
// // 🔥 filter utama per kode_id
|
||||
// $this->db->where("
|
||||
// (items.stok > 0)
|
||||
// OR
|
||||
// (
|
||||
// items.stok = 0
|
||||
// AND items.tanggal_pembelian = (
|
||||
// SELECT MAX(i2.tanggal_pembelian)
|
||||
// FROM items i2
|
||||
// WHERE i2.stok = 0
|
||||
// AND i2.kode_id = items.kode_id
|
||||
// )
|
||||
// )
|
||||
// ", null, false);
|
||||
|
||||
// // urutan
|
||||
// $this->db->order_by('items.stok = 0', 'ASC', false);
|
||||
// $this->db->order_by('items.tanggal_pembelian', 'ASC');
|
||||
|
||||
// $this->db->from('items');
|
||||
|
||||
// $data = $this->db->get()->result();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach($data as $row){
|
||||
|
||||
$btnDetail = '<button class="btn btn-info btn-sm btn-detail m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Detail</button>';
|
||||
$btnEdit = '<button class="btn btn-secondary btn-sm btn-editItem m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Edit</button>';
|
||||
$btnDelete = '<button class="btn btn-danger btn-sm btn-delete m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Delete</button>';
|
||||
|
||||
if($row->status == 'draft'){
|
||||
$btnPost = '<button class="btn btn-success btn-sm btn-updatePosting m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Posting</button>';
|
||||
$action = $btnDetail . $btnPost . $btnDelete;
|
||||
} else {
|
||||
if($this->session->userdata('role') == 'Admin') {
|
||||
$btnAdjust = '<button class="btn btn-warning btn-sm btn-adjust m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Adjust</button>';
|
||||
} else {
|
||||
$btnAdjust = '';
|
||||
}
|
||||
$action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete;
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
$no++,
|
||||
$row->tanggal_pembelian,
|
||||
$row->kode_detail,
|
||||
$row->nama_barang,
|
||||
$row->gudang ?? '-',
|
||||
$row->stok,
|
||||
number_format($row->harga_beli ?? 0, 2),
|
||||
number_format($row->harga_jual ?? 0, 2),
|
||||
$row->created_at,
|
||||
$action
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(["data"=>$result]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail_simple($id)
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get_where('items',['id'=>$id])->row()
|
||||
);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail($id)
|
||||
{
|
||||
$item = $this->db->get_where('items',['id'=>$id])->row();
|
||||
|
||||
$logs = $this->db
|
||||
->select('stock_logs.*, warehouses.nama as gudang')
|
||||
->join('warehouses','warehouses.id = stock_logs.warehouse_id','left')
|
||||
->where('item_id',$id)
|
||||
->order_by('stock_logs.id','DESC')
|
||||
->get('stock_logs')
|
||||
->result();
|
||||
|
||||
echo json_encode([
|
||||
'item'=>$item,
|
||||
'logs'=>$logs
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_accounts()
|
||||
{
|
||||
$data = $this->db
|
||||
->where_in('kode_akun', [101,102,201])
|
||||
->order_by('kode_akun','ASC')
|
||||
->get('accounts')
|
||||
->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function get_accounts_biaya()
|
||||
{
|
||||
$data = $this->db
|
||||
->where(['tipe' => 'expense', 'posisi'=> 'debit', 'kategori'=> 'laba_rugi'])
|
||||
->order_by('kode_akun','ASC')
|
||||
->get('accounts')
|
||||
->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SAVE (FIX TOTAL)
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$nama_barang = $this->input->post('nama_barang');
|
||||
$qty = (int)$this->input->post('qty');
|
||||
$harga_beli = (float)$this->input->post('harga_beli');
|
||||
$harga_jual = (float)$this->input->post('harga_jual');
|
||||
$warehouse_id = $this->input->post('warehouse_id');
|
||||
$account_kas = $this->input->post('account_kas');
|
||||
$kode_id = $this->input->post('kode_id');
|
||||
$tanggal_beli = $this->input->post('tanggal_beli');
|
||||
$status = $this->input->post('status');
|
||||
|
||||
// ================= VALIDASI =================
|
||||
if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || !$account_kas){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Data tidak lengkap / tidak valid'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ================= VALIDASI HARGA =================
|
||||
if($harga_beli > $harga_jual){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Harga beli tidak boleh lebih besar dari harga jual'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$kode = $this->db->get_where('kode_barang',['id'=>$kode_id])->row();
|
||||
|
||||
$tanggal_kode = date('dmy', strtotime($tanggal_beli));
|
||||
$kode_detail = $kode->kode_barang . '-' . $tanggal_kode;
|
||||
|
||||
$total = $qty * $harga_beli;
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// ================= INSERT ITEM =================
|
||||
$this->db->insert('items',[
|
||||
'stok' =>$qty,
|
||||
'kode_detail' =>$kode_detail,
|
||||
'kode_id' =>$kode_id,
|
||||
'nama_barang' =>$nama_barang,
|
||||
'harga_beli' =>$harga_beli,
|
||||
'harga_jual' =>$harga_jual,
|
||||
'tanggal_pembelian' => $tanggal_beli,
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
$item_id = $this->db->insert_id();
|
||||
|
||||
// ================= STOCK MASUK =================
|
||||
$this->db->insert('stock_logs',[
|
||||
'item_id'=>$item_id,
|
||||
'warehouse_id'=>$warehouse_id,
|
||||
'qty'=>$qty,
|
||||
'tipe'=>'masuk',
|
||||
'keterangan'=>'Pembelian awal',
|
||||
'created_at'=>date('Y-m-d H:i:s')
|
||||
]);
|
||||
|
||||
// ================= JURNAL =================
|
||||
$no_ref = 'BLI-'.date('Ymd').'-'.$item_id;
|
||||
|
||||
$this->db->insert('journals',[
|
||||
'tanggal'=>$tanggal_beli,
|
||||
'no_ref'=>$no_ref,
|
||||
'ref_type'=> 'new_items',
|
||||
'ref_id'=>$item_id,
|
||||
'keterangan'=>'Pembelian '.$nama_barang,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
// 🔥 Debit Persediaan
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>21, // persediaan
|
||||
'debit'=>$total,
|
||||
'kredit'=>0
|
||||
]);
|
||||
|
||||
// 🔥 Kredit Kas (DINAMIS)
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>$account_kas,
|
||||
'debit'=>0,
|
||||
'kredit'=>$total
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
// ================= CEK TRANSAKSI =================
|
||||
if ($this->db->trans_status() === FALSE){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Gagal simpan data'
|
||||
]);
|
||||
} else {
|
||||
|
||||
log_activity(
|
||||
'items',
|
||||
'create',
|
||||
'Menambahkan item: ' . $nama_barang,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status'=>true,
|
||||
'message'=>'Berhasil tambah item'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// UPDATE (FIX AKUN)
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$nama_barang = $this->input->post('nama_barang');
|
||||
$harga_baru = (float)$this->input->post('harga_beli');
|
||||
$harga_jual = (float)$this->input->post('harga_jual');
|
||||
|
||||
$item = $this->db->get_where('items',['id'=>$id])->row();
|
||||
|
||||
if(!$item){
|
||||
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
$this->db->where('id',$id)->update('items',[
|
||||
'nama_barang'=>$nama_barang,
|
||||
'harga_beli'=>$harga_baru,
|
||||
'harga_jual'=>$harga_jual
|
||||
]);
|
||||
|
||||
if($item->harga_beli != $harga_baru){
|
||||
|
||||
$stok = $this->db->select('SUM(qty * IF(tipe="masuk",1,-1)) as stok')
|
||||
->where('item_id',$id)
|
||||
->get('stock_logs')
|
||||
->row()->stok ?? 0;
|
||||
|
||||
$selisih = ($harga_baru - $item->harga_beli) * $stok;
|
||||
|
||||
if($selisih != 0){
|
||||
|
||||
$this->db->insert('journals',[
|
||||
'tanggal'=>date('Y-m-d'),
|
||||
'no_ref'=>'ADJ-'.date('YmdHis').'-'.$id,
|
||||
'keterangan'=>'Penyesuaian harga '.$item->nama_barang,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
if($selisih > 0){
|
||||
// 🔥 naik → pendapatan
|
||||
$this->db->insert_batch('journal_details',[
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>21,
|
||||
'debit'=>$selisih,
|
||||
'kredit'=>0
|
||||
],
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>65, // ✅ BENAR
|
||||
'debit'=>0,
|
||||
'kredit'=>$selisih
|
||||
]
|
||||
]);
|
||||
}else{
|
||||
// 🔥 turun → beban
|
||||
$this->db->insert_batch('journal_details',[
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>64, // ✅ BENAR
|
||||
'debit'=>abs($selisih),
|
||||
'kredit'=>0
|
||||
],
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>21,
|
||||
'debit'=>0,
|
||||
'kredit'=>abs($selisih)
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
log_activity(
|
||||
'items',
|
||||
'update',
|
||||
'Update item: ' . $nama_barang,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->update_item_stok($id);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Berhasil update + adjustment']);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
// cek item ada
|
||||
$item = $this->db->get_where('items',['id'=>$id])->row();
|
||||
|
||||
if(!$item){
|
||||
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// cek ada transaksi stock
|
||||
$cekStock = $this->db->where('item_id',$id)->count_all_results('stock_logs');
|
||||
|
||||
if($cekStock > 1){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Tidak bisa dihapus, sudah ada transaksi stok'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// hapus item
|
||||
$this->db->delete('stock_logs',['item_id'=>$id]);
|
||||
$this->db->delete('items',['id'=>$id]);
|
||||
|
||||
$j = $this->db->get_where('journals', [
|
||||
'ref_type'=>'new_items',
|
||||
'ref_id'=>$id
|
||||
])->result();
|
||||
|
||||
foreach($j as $row){
|
||||
$this->db->delete('journal_details', ['journal_id'=>$row->id]);
|
||||
$this->db->delete('journals', ['id'=>$row->id]);
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status() === FALSE){
|
||||
echo json_encode(['status'=>false,'message'=>'Gagal hapus']);
|
||||
} else {
|
||||
|
||||
log_activity(
|
||||
'items',
|
||||
'delete',
|
||||
'Hapus item ID: ' . $id,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Berhasil dihapus']);
|
||||
}
|
||||
}
|
||||
|
||||
public function posting()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
|
||||
if(!$id){
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'ID tidak valid'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// cek item ada
|
||||
$item = $this->db->get_where('items', ['id' => $id])->row();
|
||||
|
||||
if(!$item){
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Item tidak ditemukan'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// cegah double posting
|
||||
if($item->status == 'active'){
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Barang sudah diterima sebelumnya'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// update status saja
|
||||
$this->db->where('id', $id);
|
||||
$this->db->update('items', [
|
||||
'status' => 'active'
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status() === FALSE){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Gagal memproses data'
|
||||
]);
|
||||
|
||||
} else {
|
||||
|
||||
// log aktivitas
|
||||
log_activity(
|
||||
'items',
|
||||
'posting',
|
||||
'Barang diterima | ID: '.$id.' | Nama: '.$item->nama_barang,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Barang sudah diterima dan stok siap digunakan'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// KELUAR BARANG (FIX TANPA stok column)
|
||||
// =========================
|
||||
public function keluarkan()
|
||||
{
|
||||
$account_biaya = (int)$this->input->post('account_biaya');
|
||||
$tanggal_keluar = $this->input->post('tanggal_keluar');
|
||||
$warehouse_id_keluar = $this->input->post('warehouse_id_keluar');
|
||||
$barang_id = $this->input->post('barang_id');
|
||||
$qty_keluar = (int)$this->input->post('qty_keluar');
|
||||
$keterangan_keluar = $this->input->post('keterangan_keluar');
|
||||
|
||||
// =========================
|
||||
// VALIDASI
|
||||
// =========================
|
||||
if(!$barang_id || !$warehouse_id_keluar || $qty_keluar <= 0){
|
||||
echo json_encode(['status'=>false,'message'=>'Data tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// CEK ITEM
|
||||
// =========================
|
||||
$item = $this->db->get_where('items',['id'=>$barang_id])->row();
|
||||
if(!$item){
|
||||
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// HITUNG STOK
|
||||
// =========================
|
||||
$stok = $this->db->select('
|
||||
COALESCE(SUM(qty * IF(tipe="masuk",1,-1)),0) as stok
|
||||
')
|
||||
->where('item_id',$barang_id)
|
||||
->where('warehouse_id',$warehouse_id_keluar)
|
||||
->get('stock_logs')
|
||||
->row()->stok;
|
||||
|
||||
if($stok < $qty_keluar){
|
||||
echo json_encode(['status'=>false,'message'=>'Stok tidak cukup']);
|
||||
return;
|
||||
}
|
||||
|
||||
$nilai = $qty_keluar * (float)$item->harga_beli;
|
||||
|
||||
// =========================
|
||||
// TRANSACTION
|
||||
// =========================
|
||||
$this->db->trans_start();
|
||||
|
||||
// log keluar
|
||||
$this->db->insert('stock_logs',[
|
||||
'item_id'=>$barang_id,
|
||||
'warehouse_id'=>$warehouse_id_keluar,
|
||||
'qty'=>$qty_keluar,
|
||||
'tipe'=>'keluar',
|
||||
'keterangan'=>$keterangan_keluar
|
||||
]);
|
||||
|
||||
$stock_log_id = $this->db->insert_id();
|
||||
|
||||
// jurnal
|
||||
$this->db->insert('journals',[
|
||||
'tanggal'=>$tanggal_keluar,
|
||||
'no_ref'=>'GOUT-'.date('YmdHis').'-'.$barang_id,
|
||||
'keterangan'=>'Pengeluaran Barang '.$item->nama_barang . ' ( ' . $keterangan_keluar . ' )',
|
||||
'ref_type' =>'stock_logs',
|
||||
'ref_id' => $stock_log_id,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
// Debit biaya / HPP
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>$account_biaya,
|
||||
'debit'=>$nilai,
|
||||
'kredit'=>0
|
||||
]);
|
||||
|
||||
// Kredit persediaan
|
||||
$this->db->insert('journal_details',[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>21,
|
||||
'debit'=>0,
|
||||
'kredit'=>$nilai
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
// =========================
|
||||
// CEK TRANSACTION
|
||||
// =========================
|
||||
if (!$this->db->trans_status()) {
|
||||
echo json_encode(['status'=>false,'message'=>'Gagal proses']);
|
||||
return;
|
||||
}
|
||||
|
||||
log_activity(
|
||||
'items',
|
||||
'stock_out',
|
||||
'Keluar barang: ' . $item->nama_barang . ' qty: ' . $qty_keluar . ' ( ' . $keterangan_keluar . ' )',
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->update_item_stok($barang_id);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Barang berhasil dikeluarkan']);
|
||||
}
|
||||
|
||||
public function list()
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get('warehouses')->result()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function adjust()
|
||||
{
|
||||
$item_id = $this->input->post('item_id');
|
||||
$qty = (int)$this->input->post('qty');
|
||||
$warehouse_id = $this->input->post('warehouse_id');
|
||||
$ket = $this->input->post('keterangan');
|
||||
|
||||
if($qty == 0){
|
||||
echo json_encode(['status'=>false,'message'=>'Qty tidak boleh 0']);
|
||||
return;
|
||||
}
|
||||
|
||||
$item = $this->db->get_where('items',['id'=>$item_id])->row();
|
||||
|
||||
$nilai = abs($qty) * $item->harga_beli;
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// 🔥 stock log
|
||||
$this->db->insert('stock_logs',[
|
||||
'item_id'=>$item_id,
|
||||
'warehouse_id'=>$warehouse_id,
|
||||
'qty'=>abs($qty),
|
||||
'tipe'=> $qty > 0 ? 'masuk' : 'keluar',
|
||||
'keterangan'=>'Adjustment: '.$ket
|
||||
]);
|
||||
|
||||
// 🔥 jurnal
|
||||
$this->db->insert('journals',[
|
||||
'tanggal'=>date('Y-m-d'),
|
||||
'no_ref'=>'ADJ-'.date('Ymd').'-'.$item_id,
|
||||
'keterangan'=>'Penyesuaian '.$item->nama_barang,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
if($qty > 0){
|
||||
// stok naik
|
||||
$this->db->insert_batch('journal_details',[
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>21, // persediaan
|
||||
'debit'=>$nilai,
|
||||
'kredit'=>0
|
||||
],
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>65, // modal / selisih
|
||||
'debit'=>0,
|
||||
'kredit'=>$nilai
|
||||
]
|
||||
]);
|
||||
}else{
|
||||
// stok turun
|
||||
$this->db->insert_batch('journal_details',[
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>64,
|
||||
'debit'=>$nilai,
|
||||
'kredit'=>0
|
||||
],
|
||||
[
|
||||
'journal_id'=>$jid,
|
||||
'account_id'=>21,
|
||||
'debit'=>0,
|
||||
'kredit'=>$nilai
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
log_activity(
|
||||
'items',
|
||||
'adjust',
|
||||
'Adjustment item ID: ' . $item_id . ' qty: ' . $qty,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->update_item_stok($item_id);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Adjustment berhasil']);
|
||||
}
|
||||
|
||||
private function update_item_stok($item_id)
|
||||
{
|
||||
$stok = $this->db->query("
|
||||
SELECT COALESCE(SUM(qty * IF(tipe='masuk',1,-1)),0) as stok
|
||||
FROM stock_logs
|
||||
WHERE item_id = ?
|
||||
", [$item_id])->row()->stok;
|
||||
|
||||
$this->db->where('id', $item_id)
|
||||
->update('items', [
|
||||
'stok' => $stok
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_items_by_wh_id($warehouse_id)
|
||||
{
|
||||
$this->db->select('
|
||||
items.id,
|
||||
items.nama_barang,
|
||||
items.harga_jual,
|
||||
items.kode_detail,
|
||||
COALESCE(SUM(
|
||||
stock_logs.qty * IF(stock_logs.tipe="masuk",1,-1)
|
||||
),0) as stok
|
||||
');
|
||||
$this->db->where('status', 'active');
|
||||
$this->db->from('items');
|
||||
|
||||
$this->db->join('stock_logs',
|
||||
'stock_logs.item_id = items.id
|
||||
AND stock_logs.warehouse_id = '.$this->db->escape($warehouse_id),
|
||||
'left');
|
||||
|
||||
$this->db->group_by('items.id');
|
||||
|
||||
// ✅ INI KUNCINYA
|
||||
$this->db->having('stok >', 0);
|
||||
|
||||
|
||||
$data = $this->db->get()->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Jurnal extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
// if($this->session->userdata('role') != 'Admin'){
|
||||
// show_error('Akses ditolak', 403);
|
||||
// }
|
||||
|
||||
$data = ["active_menu" => "jurnal"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('jurnal/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA DATATABLE (SUDAH ADA TOTAL)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$draw = $_POST['draw'];
|
||||
$start = $_POST['start'];
|
||||
$length = $_POST['length'];
|
||||
$search = $_POST['search']['value'];
|
||||
|
||||
// ================= TOTAL ALL
|
||||
$totalAll = $this->db->count_all('journals');
|
||||
|
||||
// ================= QUERY FILTERED (HITUNG)
|
||||
$this->db->from('journals');
|
||||
|
||||
if(!empty($search)){
|
||||
$this->db->group_start();
|
||||
$this->db->like('no_ref', $search);
|
||||
$this->db->or_like('keterangan', $search);
|
||||
$this->db->or_like('tanggal', $search);
|
||||
$this->db->group_end();
|
||||
}
|
||||
|
||||
$filtered = $this->db->count_all_results();
|
||||
|
||||
// ================= QUERY DATA (AMBIL DATA)
|
||||
$this->db->select('
|
||||
journals.id,
|
||||
journals.tanggal,
|
||||
journals.no_ref,
|
||||
journals.keterangan,
|
||||
COALESCE(SUM(journal_details.debit),0) as total_debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) as total_kredit
|
||||
');
|
||||
$this->db->from('journals');
|
||||
$this->db->join('journal_details','journal_details.journal_id = journals.id','LEFT');
|
||||
|
||||
if(!empty($search)){
|
||||
$this->db->group_start();
|
||||
$this->db->like('journals.no_ref', $search);
|
||||
$this->db->or_like('journals.keterangan', $search);
|
||||
$this->db->or_like('journals.tanggal', $search);
|
||||
$this->db->group_end();
|
||||
}
|
||||
|
||||
$this->db->group_by('journals.id');
|
||||
$this->db->order_by('journals.tanggal','DESC');
|
||||
$this->db->order_by('journals.created_at','DESC');
|
||||
$this->db->limit($length, $start);
|
||||
|
||||
$query = $this->db->get()->result();
|
||||
|
||||
// ================= FORMAT
|
||||
$data = [];
|
||||
$no = $start;
|
||||
|
||||
foreach ($query as $row) {
|
||||
$no++;
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
|
||||
$aksi = '
|
||||
<button class="btn btn-sm btn-info btn-detail" data-id="'.$row->id.'">Detail</button>
|
||||
';
|
||||
|
||||
}
|
||||
else {
|
||||
$aksi = '
|
||||
<button class="btn btn-sm btn-info btn-detail" data-id="'.$row->id.'">Detail</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
';
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
$no,
|
||||
$row->tanggal,
|
||||
$row->no_ref,
|
||||
$row->keterangan,
|
||||
number_format($row->total_debit,0,',','.'),
|
||||
number_format($row->total_kredit,0,',','.'),
|
||||
$aksi
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"draw" => $draw,
|
||||
"recordsTotal" => $totalAll,
|
||||
"recordsFiltered" => $filtered,
|
||||
"data" => $data
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET ACCOUNTS
|
||||
// =========================
|
||||
public function get_accounts()
|
||||
{
|
||||
$data = $this->db->order_by('kode_akun','ASC')->get('accounts')->result();
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SAVE
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$tanggal = $this->input->post('tanggal');
|
||||
$no_ref = 'JR-' . date('YmdHis');
|
||||
$keterangan = $this->input->post('keterangan');
|
||||
|
||||
$accounts = $this->input->post('account_id');
|
||||
$debit = $this->input->post('debit');
|
||||
$kredit = $this->input->post('kredit');
|
||||
|
||||
if (array_sum($debit) != array_sum($kredit)) {
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Debit & Kredit tidak balance!'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
$this->db->insert('journals', [
|
||||
'tanggal' => $tanggal,
|
||||
'no_ref' => $no_ref,
|
||||
'keterangan' => $keterangan,
|
||||
'created_by' => $this->session->userdata('user_id')
|
||||
]);
|
||||
|
||||
$journal_id = $this->db->insert_id();
|
||||
|
||||
for ($i=0; $i<count($accounts); $i++) {
|
||||
if ($accounts[$i] == '') continue;
|
||||
|
||||
$this->db->insert('journal_details', [
|
||||
'journal_id' => $journal_id,
|
||||
'account_id' => $accounts[$i],
|
||||
'debit' => $debit[$i] ?: 0,
|
||||
'kredit' => $kredit[$i] ?: 0
|
||||
]);
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status() === FALSE) {
|
||||
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Gagal simpan jurnal'
|
||||
]);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
log_activity(
|
||||
'Jurnal',
|
||||
'create',
|
||||
'Membuat jurnal No Ref: '.$no_ref,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status'=>true,
|
||||
'message'=>'Jurnal berhasil disimpan'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail($id)
|
||||
{
|
||||
$header = $this->db->get_where('journals',['id'=>$id])->row();
|
||||
|
||||
$detail = $this->db
|
||||
->select('accounts.kode_akun, accounts.nama_akun, journal_details.*')
|
||||
->from('journal_details')
|
||||
->join('accounts','accounts.id = journal_details.account_id')
|
||||
->where('journal_id',$id)
|
||||
->get()->result();
|
||||
|
||||
// print_r($header);
|
||||
|
||||
$userid = $header->created_by;
|
||||
$user = $this->db
|
||||
->select('nama')
|
||||
->where('id', $userid)
|
||||
->get('users')
|
||||
->row();
|
||||
|
||||
echo json_encode([
|
||||
'header'=>$header,
|
||||
'detail'=>$detail,
|
||||
'user' =>$user
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
$journal = $this->db->get_where('journals', ['id' => $id])->row();
|
||||
|
||||
// VALIDASI
|
||||
if (!$journal) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Jurnal tidak ditemukan'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// =========================
|
||||
// HANDLE STOCK LOGS
|
||||
// =========================
|
||||
if ($journal->ref_type == 'stock_logs') {
|
||||
|
||||
$stock = $this->db
|
||||
->select('item_id, warehouse_id')
|
||||
->where('id', $journal->ref_id)
|
||||
->get('stock_logs')
|
||||
->row();
|
||||
|
||||
if ($stock) {
|
||||
|
||||
// hapus log stok
|
||||
$this->db->delete('stock_logs', ['id' => $journal->ref_id]);
|
||||
|
||||
// update stok ulang (pakai item_id saja biar gak ganggu sistem lama)
|
||||
$this->update_item_stok($stock->item_id);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE JOURNAL
|
||||
// =========================
|
||||
$this->db->delete('journal_details', ['journal_id' => $id]);
|
||||
$this->db->delete('journals', ['id' => $id]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
// =========================
|
||||
// VALIDASI TRANSAKSI
|
||||
// =========================
|
||||
if (!$this->db->trans_status()) {
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Gagal hapus jurnal'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
log_activity(
|
||||
'Jurnal',
|
||||
'delete',
|
||||
'Menghapus jurnal no_ref: ' . $journal->no_ref . ' ket: ' . $journal->keterangan,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true, // ✅ FIX
|
||||
'message' => 'Jurnal berhasil dihapus',
|
||||
'journal' => $journal->ref_type
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_base_jurnal()
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->order_by('kode','ASC')->get('base_journal')->result()
|
||||
);
|
||||
}
|
||||
|
||||
public function get_base_jurnal_detail($id)
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->where('base_journal_id',$id)
|
||||
->get('base_journal_detail')
|
||||
->result()
|
||||
);
|
||||
}
|
||||
|
||||
private function update_item_stok($item_id)
|
||||
{
|
||||
$stok = $this->db->query("
|
||||
SELECT COALESCE(SUM(qty * IF(tipe='masuk',1,-1)),0) as stok
|
||||
FROM stock_logs
|
||||
WHERE item_id = ?
|
||||
", [$item_id])->row()->stok;
|
||||
|
||||
$this->db->where('id', $item_id)
|
||||
->update('items', [
|
||||
'stok' => $stok
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Kodebarang extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ["active_menu" => "kode_barang"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('kode_barang/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA (DATATABLE)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$data = $this->db->get('kode_barang')->result();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$result[] = [
|
||||
$no++,
|
||||
$row->kode_barang,
|
||||
$row->nama,
|
||||
$row->limit_stock,
|
||||
'
|
||||
<button class="btn btn-sm btn-secondary btn-editkode" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(["data"=>$result]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail($id)
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get_where('kode_barang',['id'=>$id])->row()
|
||||
);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SAVE
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$nama = $this->input->post('nama');
|
||||
$kode = $this->input->post('kode');
|
||||
$limit_stock = $this->input->post('limit_stock');
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->insert('kode_barang', [
|
||||
'kode_barang' => $kode,
|
||||
'nama' => $nama,
|
||||
'limit_stock' => $limit_stock
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'create',
|
||||
'Menambahkan kode barang: ' . $nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// UPDATE
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$kode = $this->input->post('kode');
|
||||
$nama = $this->input->post('nama');
|
||||
$limit_stock = $this->input->post('limit_stock');
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->where('id',$id)->update('kode_barang', [
|
||||
'kode_barang' => $kode,
|
||||
'nama' => $nama,
|
||||
'limit_stock' => $limit_stock
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'update',
|
||||
'Update kode barang ID: ' . $id . ' nama: ' . $nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
// optional: cek relasi ke items
|
||||
$cek = $this->db->get_where('items',['warehouse_id'=>$id])->num_rows();
|
||||
|
||||
if($cek > 0){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->delete('kode_barang',['id'=>$id]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'delete',
|
||||
'Hapus gudang ID: ' . $id,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
|
||||
}
|
||||
|
||||
|
||||
public function list()
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get('kode_barang')->result()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Labarugi extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "laba_rugi"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('laporan/laba_rugi');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$tanggal_dari = $this->input->get('tanggal_dari');
|
||||
$tanggal_sampai = $this->input->get('tanggal_sampai');
|
||||
|
||||
$this->db->select('
|
||||
a.id,
|
||||
a.kode_akun,
|
||||
a.nama_akun,
|
||||
a.tipe,
|
||||
COALESCE(SUM(jd.debit),0) as debit,
|
||||
COALESCE(SUM(jd.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts a');
|
||||
|
||||
// ================= JOIN
|
||||
$this->db->join('journal_details jd','a.id = jd.account_id','LEFT');
|
||||
$this->db->join('journals j','j.id = jd.journal_id','LEFT');
|
||||
|
||||
// ================= FILTER AKUN
|
||||
$this->db->where('a.kategori','laba_rugi');
|
||||
$this->db->where('a.is_active',1);
|
||||
|
||||
// ================= FILTER TANGGAL (AMAN)
|
||||
if($tanggal_dari){
|
||||
$this->db->where('(j.tanggal >= "'.$tanggal_dari.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
if($tanggal_sampai){
|
||||
$this->db->where('(j.tanggal <= "'.$tanggal_sampai.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
$this->db->group_by('a.id');
|
||||
$this->db->order_by('a.kode_akun','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
if ($r->tipe == 'revenue') {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
$revenue += $saldo;
|
||||
}
|
||||
|
||||
if ($r->tipe == 'expense') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
$expense += $saldo;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'data' => $rows,
|
||||
'revenue' => $revenue,
|
||||
'expense' => $expense,
|
||||
'laba' => $revenue - $expense
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Leaverequests extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// PAGE
|
||||
// ==========================================
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "leave_requests"
|
||||
];
|
||||
|
||||
$data['employees'] = $this->db
|
||||
->where('is_active',1)
|
||||
->order_by('full_name','ASC')
|
||||
->get('k_employees')
|
||||
->result();
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/leave_requests', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// DATATABLE
|
||||
// ==========================================
|
||||
public function get_data()
|
||||
{
|
||||
$this->db->select('
|
||||
lr.*,
|
||||
e.full_name,
|
||||
e.fingerprint_user_id
|
||||
');
|
||||
|
||||
$this->db->from('k_leave_requests lr');
|
||||
|
||||
$this->db->join(
|
||||
'k_employees e',
|
||||
'e.id=lr.employee_id',
|
||||
'left'
|
||||
);
|
||||
|
||||
$this->db->order_by('lr.id','DESC');
|
||||
|
||||
$list = $this->db->get()->result();
|
||||
|
||||
$data = [];
|
||||
$no = 1;
|
||||
|
||||
foreach($list as $r){
|
||||
|
||||
// =====================================
|
||||
// LEAVE TYPE
|
||||
// =====================================
|
||||
$leave_type = '-';
|
||||
|
||||
if($r->leave_type == 'annual'){
|
||||
$leave_type = 'Cuti Tahunan';
|
||||
}
|
||||
|
||||
if($r->leave_type == 'sick'){
|
||||
$leave_type = 'Sakit';
|
||||
}
|
||||
|
||||
if($r->leave_type == 'permit'){
|
||||
$leave_type = 'Izin';
|
||||
}
|
||||
|
||||
if($r->leave_type == 'maternity'){
|
||||
$leave_type = 'Cuti Melahirkan';
|
||||
}
|
||||
|
||||
if($r->leave_type == 'unpaid'){
|
||||
$leave_type = 'Cuti Tidak Dibayar';
|
||||
}
|
||||
|
||||
// =====================================
|
||||
// STATUS
|
||||
// =====================================
|
||||
if($r->approval_status == 'approved'){
|
||||
|
||||
$status = '
|
||||
<span class="badge bg-success">
|
||||
Disetujui
|
||||
</span>
|
||||
';
|
||||
|
||||
} elseif($r->approval_status == 'rejected'){
|
||||
|
||||
$status = '
|
||||
<span class="badge bg-danger">
|
||||
Ditolak
|
||||
</span>
|
||||
';
|
||||
|
||||
} else {
|
||||
|
||||
$status = '
|
||||
<span class="badge bg-warning text-dark">
|
||||
Pending
|
||||
</span>
|
||||
';
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
$no++,
|
||||
$r->fingerprint_user_id,
|
||||
$r->full_name,
|
||||
$leave_type,
|
||||
$r->start_date,
|
||||
$r->end_date,
|
||||
$r->total_days . ' Hari',
|
||||
$status,
|
||||
'
|
||||
<button class="btn btn-warning btn-sm btn-edit"
|
||||
data-id="'.$r->id.'">
|
||||
|
||||
Edit
|
||||
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger btn-sm btn-delete"
|
||||
data-id="'.$r->id.'">
|
||||
|
||||
Delete
|
||||
|
||||
</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"data" => $data
|
||||
]);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// DETAIL
|
||||
// ==========================================
|
||||
public function detail($id)
|
||||
{
|
||||
$data = $this->db
|
||||
->get_where(
|
||||
'k_leave_requests',
|
||||
['id'=>$id]
|
||||
)
|
||||
->row();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// SAVE
|
||||
// ==========================================
|
||||
public function save()
|
||||
{
|
||||
try{
|
||||
|
||||
$start_date = $this->input->post('start_date');
|
||||
$end_date = $this->input->post('end_date');
|
||||
|
||||
$total_days = (
|
||||
strtotime($end_date)
|
||||
-
|
||||
strtotime($start_date)
|
||||
) / 86400 + 1;
|
||||
|
||||
$data = [
|
||||
|
||||
'employee_id' => $this->input->post('employee_id'),
|
||||
|
||||
'leave_type' => $this->input->post('leave_type'),
|
||||
|
||||
'start_date' => $start_date,
|
||||
|
||||
'end_date' => $end_date,
|
||||
|
||||
'total_days' => $total_days,
|
||||
|
||||
'reason' => $this->input->post('reason'),
|
||||
|
||||
'approval_status' => $this->input->post('approval_status')
|
||||
];
|
||||
|
||||
$this->db->insert(
|
||||
'k_leave_requests',
|
||||
$data
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message'=> 'Leave request berhasil ditambahkan'
|
||||
]);
|
||||
|
||||
} catch(Exception $e){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message'=> $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// UPDATE
|
||||
// ==========================================
|
||||
public function update()
|
||||
{
|
||||
try{
|
||||
|
||||
$id = $this->input->post('id');
|
||||
|
||||
$start_date = $this->input->post('start_date');
|
||||
$end_date = $this->input->post('end_date');
|
||||
|
||||
$total_days = (
|
||||
strtotime($end_date)
|
||||
-
|
||||
strtotime($start_date)
|
||||
) / 86400 + 1;
|
||||
|
||||
$data = [
|
||||
|
||||
'employee_id' => $this->input->post('employee_id'),
|
||||
|
||||
'leave_type' => $this->input->post('leave_type'),
|
||||
|
||||
'start_date' => $start_date,
|
||||
|
||||
'end_date' => $end_date,
|
||||
|
||||
'total_days' => $total_days,
|
||||
|
||||
'reason' => $this->input->post('reason'),
|
||||
|
||||
'approval_status' => $this->input->post('approval_status')
|
||||
];
|
||||
|
||||
$this->db->where('id',$id);
|
||||
|
||||
$this->db->update(
|
||||
'k_leave_requests',
|
||||
$data
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message'=> 'Leave request berhasil diupdate'
|
||||
]);
|
||||
|
||||
} catch(Exception $e){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message'=> $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// DELETE
|
||||
// ==========================================
|
||||
public function delete($id)
|
||||
{
|
||||
$this->db->delete(
|
||||
'k_leave_requests',
|
||||
['id'=>$id]
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message'=> 'Leave request berhasil dihapus'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Lokasiasset extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ["active_menu" => "lokasiasset"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('lokasi_asset/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA (DATATABLE)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$data = $this->db->get('lokasi_asset')->result();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$result[] = [
|
||||
$no++,
|
||||
$row->nama,
|
||||
'
|
||||
<button class="btn btn-sm btn-secondary btn-editgudang" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(["data"=>$result]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail($id)
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get_where('lokasi_asset',['id'=>$id])->row()
|
||||
);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SAVE
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$nama = $this->input->post('nama');
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->insert('lokasi_asset', [
|
||||
'nama' => $nama
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'create',
|
||||
'Tambah gudang '.$nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// UPDATE
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$nama = $this->input->post('nama');
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->where('id',$id)->update('lokasi_asset', [
|
||||
'nama' => $nama
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'update',
|
||||
'Update gudang '.$nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
// optional: cek relasi ke items
|
||||
$cek = $this->db->get_where('assets',['lokasi_asset_id'=>$id])->num_rows();
|
||||
|
||||
if($cek > 0){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->delete('lokasi_asset',['id'=>$id]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'delete',
|
||||
'Delete gudang '.$nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
|
||||
}
|
||||
|
||||
|
||||
public function list()
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get('lokasi_asset')->result()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Neraca extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "neraca"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('laporan/neraca');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// LABA BERJALAN (PAKAI TANGGAL JOURNAL)
|
||||
// =========================
|
||||
private function get_laba_berjalan($tanggal_dari = null, $tanggal_sampai = null)
|
||||
{
|
||||
$this->db->select('
|
||||
accounts.tipe,
|
||||
COALESCE(SUM(jd.debit),0) as debit,
|
||||
COALESCE(SUM(jd.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details jd','jd.account_id = accounts.id','LEFT');
|
||||
$this->db->join('journals j','j.id = jd.journal_id','LEFT');
|
||||
|
||||
$this->db->where_in('accounts.tipe',['revenue','expense']);
|
||||
|
||||
// ================= FILTER TANGGAL
|
||||
if($tanggal_dari){
|
||||
$this->db->where('(j.tanggal >= "'.$tanggal_dari.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
if($tanggal_sampai){
|
||||
$this->db->where('(j.tanggal <= "'.$tanggal_sampai.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
if ($r->tipe == 'revenue') {
|
||||
$revenue += ($r->kredit - $r->debit);
|
||||
} else {
|
||||
$expense += ($r->debit - $r->kredit);
|
||||
}
|
||||
}
|
||||
|
||||
return $revenue - $expense;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA NERACA
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$tanggal_dari = $this->input->get('tanggal_dari');
|
||||
$tanggal_sampai = $this->input->get('tanggal_sampai');
|
||||
|
||||
$this->db->select('
|
||||
a.id,
|
||||
a.kode_akun,
|
||||
a.nama_akun,
|
||||
a.tipe,
|
||||
a.posisi,
|
||||
a.is_kontra,
|
||||
COALESCE(SUM(jd.debit),0) as debit,
|
||||
COALESCE(SUM(jd.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts a');
|
||||
$this->db->join('journal_details jd','jd.account_id = a.id','LEFT');
|
||||
$this->db->join('journals j','j.id = jd.journal_id','LEFT');
|
||||
|
||||
$this->db->where('a.kategori','neraca');
|
||||
|
||||
// ================= FILTER TANGGAL
|
||||
if($tanggal_dari){
|
||||
$this->db->where('(j.tanggal >= "'.$tanggal_dari.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
if($tanggal_sampai){
|
||||
$this->db->where('(j.tanggal <= "'.$tanggal_sampai.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
$this->db->group_by('a.id');
|
||||
$this->db->order_by('a.kode_akun','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$activa = [];
|
||||
$pasiva = [];
|
||||
|
||||
$total_activa = 0;
|
||||
$total_pasiva = 0;
|
||||
|
||||
// ================= LABA BERJALAN
|
||||
$laba = $this->get_laba_berjalan($tanggal_dari, $tanggal_sampai);
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
// ================= SALDO NORMAL
|
||||
if ($r->posisi == 'debit') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
} else {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
}
|
||||
|
||||
// ================= KONTRA ACCOUNT
|
||||
if ($r->is_kontra == 1) {
|
||||
$saldo = -$saldo;
|
||||
}
|
||||
|
||||
// ================= LABA BERJALAN
|
||||
if ($r->kode_akun == '302') {
|
||||
$saldo = $laba;
|
||||
}
|
||||
|
||||
// ================= ACTIVA
|
||||
if ($r->tipe == 'asset') {
|
||||
|
||||
$activa[] = [
|
||||
'kode' => $r->kode_akun,
|
||||
'nama' => $r->nama_akun,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
|
||||
$total_activa += $saldo;
|
||||
}
|
||||
|
||||
// ================= PASIVA
|
||||
if (in_array($r->tipe, ['liability','equity'])) {
|
||||
|
||||
$pasiva[] = [
|
||||
'kode' => $r->kode_akun,
|
||||
'nama' => $r->nama_akun,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
|
||||
$total_pasiva += $saldo;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'activa' => $activa,
|
||||
'pasiva' => $pasiva,
|
||||
'total_activa' => $total_activa,
|
||||
'total_pasiva' => $total_pasiva,
|
||||
'laba' => $laba,
|
||||
'selisih' => $total_activa - $total_pasiva
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Neracasaldo extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "neraca_saldo"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('laporan/neraca_saldo');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// HITUNG LABA BERJALAN (REALTIME)
|
||||
// =========================
|
||||
private function get_laba_berjalan()
|
||||
{
|
||||
$this->db->select('
|
||||
accounts.tipe,
|
||||
COALESCE(SUM(journal_details.debit),0) as debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details','journal_details.account_id = accounts.id','LEFT');
|
||||
$this->db->where_in('accounts.tipe',['revenue','expense']);
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
if ($r->tipe == 'revenue') {
|
||||
$revenue += ($r->kredit - $r->debit);
|
||||
} else {
|
||||
$expense += ($r->debit - $r->kredit);
|
||||
}
|
||||
}
|
||||
|
||||
return $revenue - $expense;
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$this->db->select('
|
||||
accounts.kode_akun,
|
||||
accounts.nama_akun,
|
||||
accounts.tipe,
|
||||
accounts.posisi,
|
||||
COALESCE(SUM(journal_details.debit),0) as debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details','journal_details.account_id = accounts.id','LEFT');
|
||||
|
||||
// OPTIONAL (best practice)
|
||||
$this->db->where('accounts.is_active',1);
|
||||
$this->db->where('accounts.kategori','neraca');
|
||||
|
||||
$this->db->group_by('accounts.id');
|
||||
$this->db->order_by('accounts.kode_akun','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
// =========================
|
||||
// HITUNG LABA BERJALAN
|
||||
// =========================
|
||||
$laba = $this->get_laba_berjalan();
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
// ================= SALDO NORMAL
|
||||
if ($r->posisi == 'debit') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
} else {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
}
|
||||
|
||||
// ================= OVERRIDE LABA BERJALAN
|
||||
if ($r->kode_akun == '302') {
|
||||
$saldo = $laba;
|
||||
}
|
||||
|
||||
// ================= NORMALISASI KE KOLOM
|
||||
if ($saldo >= 0 AND $r->posisi == 'debit') {
|
||||
$r->saldo_debit = $saldo;
|
||||
$r->saldo_kredit = 0;
|
||||
} else {
|
||||
$r->saldo_debit = 0;
|
||||
$r->saldo_kredit = abs($saldo);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($rows);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Payroll extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// INDEX
|
||||
// =====================================================
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
'active_menu' => 'payroll'
|
||||
];
|
||||
|
||||
$this->load->view('partials/header',$data);
|
||||
$this->load->view('employees/payroll');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// GET DATA
|
||||
// =====================================================
|
||||
public function get_data()
|
||||
{
|
||||
$month = $this->input->post('month');
|
||||
|
||||
$this->db->select("
|
||||
p.*,
|
||||
e.full_name,
|
||||
pp.period_name,
|
||||
pp.start_date,
|
||||
pp.end_date
|
||||
");
|
||||
|
||||
$this->db->from('k_payrolls p');
|
||||
|
||||
$this->db->join(
|
||||
'k_employees e',
|
||||
'e.id = p.employee_id'
|
||||
);
|
||||
|
||||
$this->db->join(
|
||||
'k_payroll_periods pp',
|
||||
'pp.id = p.payroll_period_id'
|
||||
);
|
||||
|
||||
if($month){
|
||||
|
||||
$this->db->where(
|
||||
'DATE_FORMAT(pp.start_date,"%Y-%m")',
|
||||
$month
|
||||
);
|
||||
}
|
||||
|
||||
$this->db->order_by(
|
||||
'e.full_name',
|
||||
'ASC'
|
||||
);
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$data = [];
|
||||
|
||||
$no = 1;
|
||||
|
||||
foreach($rows as $row){
|
||||
|
||||
$pendapatan =
|
||||
(
|
||||
$row->allowance_amount
|
||||
+
|
||||
$row->bonus_amount
|
||||
+
|
||||
$row->overtime_amount
|
||||
);
|
||||
|
||||
$potongan =
|
||||
(
|
||||
$row->deduction_amount
|
||||
+
|
||||
$row->bpjs_kesehatan_amount
|
||||
+
|
||||
$row->bpjs_ketenagakerjaan_amount
|
||||
+
|
||||
$row->tax_amount
|
||||
+
|
||||
$row->attendance_cut_amount
|
||||
);
|
||||
|
||||
$status = '';
|
||||
|
||||
if($row->payment_status == 'paid'){
|
||||
|
||||
$status = '
|
||||
<span class="badge bg-success">
|
||||
Paid
|
||||
</span>
|
||||
';
|
||||
|
||||
} else {
|
||||
|
||||
$status = '
|
||||
<span class="badge bg-warning text-dark">
|
||||
Draft
|
||||
</span>
|
||||
';
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
|
||||
$no++,
|
||||
|
||||
$row->full_name,
|
||||
|
||||
'Rp '.number_format(
|
||||
$row->basic_salary,
|
||||
0,
|
||||
',',
|
||||
'.'
|
||||
),
|
||||
|
||||
'Rp '.number_format(
|
||||
$pendapatan,
|
||||
0,
|
||||
',',
|
||||
'.'
|
||||
),
|
||||
|
||||
'Rp '.number_format(
|
||||
$potongan,
|
||||
0,
|
||||
',',
|
||||
'.'
|
||||
),
|
||||
|
||||
'Rp '.number_format(
|
||||
$row->total_salary,
|
||||
0,
|
||||
',',
|
||||
'.'
|
||||
),
|
||||
|
||||
$status,
|
||||
|
||||
'
|
||||
<button
|
||||
class="btn btn-sm btn-warning btn-edit"
|
||||
data-id="'.$row->id.'">
|
||||
|
||||
<i class="fa fa-edit"></i>
|
||||
Edit
|
||||
|
||||
</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// DETAIL
|
||||
// =====================================================
|
||||
public function detail($id)
|
||||
{
|
||||
$this->db->select("
|
||||
p.*,
|
||||
e.full_name
|
||||
");
|
||||
|
||||
$this->db->from('k_payrolls p');
|
||||
|
||||
$this->db->join(
|
||||
'k_employees e',
|
||||
'e.id = p.employee_id'
|
||||
);
|
||||
|
||||
$this->db->where(
|
||||
'p.id',
|
||||
$id
|
||||
);
|
||||
|
||||
$payroll = $this->db->get()->row();
|
||||
|
||||
if(!$payroll){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Payroll tidak ditemukan'
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$items = $this->db
|
||||
->where('payroll_id',$id)
|
||||
->order_by('id','ASC')
|
||||
->get('k_payroll_items')
|
||||
->result();
|
||||
|
||||
echo json_encode([
|
||||
|
||||
'status' => true,
|
||||
|
||||
'payroll' => $payroll,
|
||||
|
||||
'employee' => [
|
||||
'full_name' => $payroll->full_name
|
||||
],
|
||||
|
||||
'items' => $items
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// SAVE ITEMS
|
||||
// =====================================================
|
||||
public function save_items()
|
||||
{
|
||||
$payrollId = $this->input->post('payroll_id');
|
||||
|
||||
$items = $this->input->post('items');
|
||||
|
||||
$payroll = $this->db
|
||||
->where('id',$payrollId)
|
||||
->get('k_payrolls')
|
||||
->row();
|
||||
|
||||
if(!$payroll){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Payroll tidak ditemukan'
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// DELETE OLD ITEMS
|
||||
// =========================================
|
||||
$this->db
|
||||
->where('payroll_id',$payrollId)
|
||||
->delete('k_payroll_items');
|
||||
|
||||
$allowance = 0;
|
||||
$bonus = 0;
|
||||
$overtime = 0;
|
||||
$deduction = 0;
|
||||
$bpjs = 0;
|
||||
$tax = 0;
|
||||
|
||||
if($items){
|
||||
|
||||
foreach($items as $item){
|
||||
|
||||
$amount = (float)$item['amount'];
|
||||
|
||||
$insert = [
|
||||
|
||||
'payroll_id' => $payrollId,
|
||||
|
||||
'item_type' => $item['item_type'],
|
||||
|
||||
'item_name' => $item['item_name'],
|
||||
|
||||
'amount' => $amount
|
||||
];
|
||||
|
||||
$this->db->insert(
|
||||
'k_payroll_items',
|
||||
$insert
|
||||
);
|
||||
|
||||
switch($item['item_type']){
|
||||
|
||||
case 'allowance':
|
||||
$allowance += $amount;
|
||||
break;
|
||||
|
||||
case 'bonus':
|
||||
$bonus += $amount;
|
||||
break;
|
||||
|
||||
case 'overtime':
|
||||
$overtime += $amount;
|
||||
break;
|
||||
|
||||
case 'deduction':
|
||||
$deduction += $amount;
|
||||
break;
|
||||
|
||||
case 'bpjs':
|
||||
$bpjs += $amount;
|
||||
break;
|
||||
|
||||
case 'tax':
|
||||
$tax += $amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// TOTAL
|
||||
// =========================================
|
||||
$totalSalary =
|
||||
(
|
||||
$payroll->basic_salary
|
||||
+
|
||||
$allowance
|
||||
+
|
||||
$bonus
|
||||
+
|
||||
$overtime
|
||||
)
|
||||
-
|
||||
(
|
||||
$deduction
|
||||
+
|
||||
$bpjs
|
||||
+
|
||||
$tax
|
||||
+
|
||||
$payroll->attendance_cut_amount
|
||||
);
|
||||
|
||||
// =========================================
|
||||
// UPDATE PAYROLL
|
||||
// =========================================
|
||||
$this->db
|
||||
->where('id',$payrollId)
|
||||
->update(
|
||||
'k_payrolls',
|
||||
[
|
||||
|
||||
'allowance_amount' => $allowance,
|
||||
|
||||
'bonus_amount' => $bonus,
|
||||
|
||||
'overtime_amount' => $overtime,
|
||||
|
||||
'deduction_amount' => $deduction,
|
||||
|
||||
'bpjs_kesehatan_amount' => $bpjs,
|
||||
|
||||
'tax_amount' => $tax,
|
||||
|
||||
'total_salary' => $totalSalary
|
||||
]
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Payroll berhasil disimpan'
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// GENERATE PAYROLL
|
||||
// =====================================================
|
||||
public function generate_payroll()
|
||||
{
|
||||
$month = $this->input->post('month');
|
||||
|
||||
if(!$month){
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Bulan wajib dipilih'
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$startDate = date(
|
||||
'Y-m-01',
|
||||
strtotime($month)
|
||||
);
|
||||
|
||||
$endDate = date(
|
||||
'Y-m-t',
|
||||
strtotime($month)
|
||||
);
|
||||
|
||||
// =========================================
|
||||
// PERIOD
|
||||
// =========================================
|
||||
$period = $this->db
|
||||
->where('start_date',$startDate)
|
||||
->where('end_date',$endDate)
|
||||
->get('k_payroll_periods')
|
||||
->row();
|
||||
|
||||
if(!$period){
|
||||
|
||||
$this->db->insert(
|
||||
'k_payroll_periods',
|
||||
[
|
||||
|
||||
'period_name' => date(
|
||||
'F Y',
|
||||
strtotime($startDate)
|
||||
),
|
||||
|
||||
'start_date' => $startDate,
|
||||
|
||||
'end_date' => $endDate,
|
||||
|
||||
'status' => 'draft',
|
||||
|
||||
'generated_at' => date('Y-m-d H:i:s')
|
||||
]
|
||||
);
|
||||
|
||||
$periodId = $this->db->insert_id();
|
||||
|
||||
} else {
|
||||
|
||||
$periodId = $period->id;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// EMPLOYEES
|
||||
// =========================================
|
||||
$employees = $this->db
|
||||
->where('is_active',1)
|
||||
->get('k_employees')
|
||||
->result();
|
||||
|
||||
foreach($employees as $employee){
|
||||
|
||||
// =====================================
|
||||
// ATTENDANCE
|
||||
// =====================================
|
||||
$attendance = $this->db
|
||||
->select("
|
||||
COUNT(*) as total_work_days,
|
||||
|
||||
SUM(attendance_status='present')
|
||||
as total_present,
|
||||
|
||||
SUM(attendance_status='alpha')
|
||||
as total_alpha,
|
||||
|
||||
SUM(overtime_hours)
|
||||
as total_overtime_hours
|
||||
")
|
||||
->where('employee_id',$employee->id)
|
||||
->where('attendance_date >=',$startDate)
|
||||
->where('attendance_date <=',$endDate)
|
||||
->get('k_attendances')
|
||||
->row();
|
||||
|
||||
$dailySalary =
|
||||
$employee->basic_salary / 30;
|
||||
|
||||
$alphaCut =
|
||||
(
|
||||
$attendance->total_alpha
|
||||
* $dailySalary
|
||||
);
|
||||
|
||||
$overtimeRate = 25000;
|
||||
|
||||
$overtimeAmount =
|
||||
(
|
||||
$attendance->total_overtime_hours
|
||||
* $overtimeRate
|
||||
);
|
||||
|
||||
$totalSalary =
|
||||
(
|
||||
$employee->basic_salary
|
||||
+ $overtimeAmount
|
||||
)
|
||||
- $alphaCut;
|
||||
|
||||
$exist = $this->db
|
||||
->where(
|
||||
'payroll_period_id',
|
||||
$periodId
|
||||
)
|
||||
->where(
|
||||
'employee_id',
|
||||
$employee->id
|
||||
)
|
||||
->get('k_payrolls')
|
||||
->row();
|
||||
|
||||
$data = [
|
||||
|
||||
'payroll_period_id' => $periodId,
|
||||
|
||||
'employee_id' => $employee->id,
|
||||
|
||||
'basic_salary' => $employee->basic_salary,
|
||||
|
||||
'total_work_days' => (
|
||||
$attendance->total_work_days ?? 0
|
||||
),
|
||||
|
||||
'total_present' => (
|
||||
$attendance->total_present ?? 0
|
||||
),
|
||||
|
||||
'total_alpha' => (
|
||||
$attendance->total_alpha ?? 0
|
||||
),
|
||||
|
||||
'total_overtime_hours' => (
|
||||
$attendance->total_overtime_hours ?? 0
|
||||
),
|
||||
|
||||
'attendance_cut_amount' => $alphaCut,
|
||||
|
||||
'overtime_amount' => $overtimeAmount,
|
||||
|
||||
'total_salary' => $totalSalary
|
||||
];
|
||||
|
||||
if($exist){
|
||||
|
||||
$this->db
|
||||
->where('id',$exist->id)
|
||||
->update(
|
||||
'k_payrolls',
|
||||
$data
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
$this->db->insert(
|
||||
'k_payrolls',
|
||||
$data
|
||||
);
|
||||
|
||||
$payrollId = $this->db->insert_id();
|
||||
|
||||
// =================================
|
||||
// AUTO ITEM OVERTIME
|
||||
// =================================
|
||||
if($overtimeAmount > 0){
|
||||
|
||||
$this->db->insert(
|
||||
'k_payroll_items',
|
||||
[
|
||||
|
||||
'payroll_id' => $payrollId,
|
||||
|
||||
'item_type' => 'overtime',
|
||||
|
||||
'item_name' => 'Lembur',
|
||||
|
||||
'amount' => $overtimeAmount
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// =================================
|
||||
// AUTO ITEM ALPHA
|
||||
// =================================
|
||||
if($alphaCut > 0){
|
||||
|
||||
$this->db->insert(
|
||||
'k_payroll_items',
|
||||
[
|
||||
|
||||
'payroll_id' => $payrollId,
|
||||
|
||||
'item_type' => 'deduction',
|
||||
|
||||
'item_name' => 'Potongan Alpha',
|
||||
|
||||
'amount' => $alphaCut
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Payroll berhasil digenerate'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Pesan extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->database();
|
||||
|
||||
// Cek apakah user sudah login
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('login');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if (!check_permission('pesan', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$data = [
|
||||
"active_menu" => "pesan"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('pesan/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// DATATABLE SERVERSIDE
|
||||
// =========================================================
|
||||
public function get_data()
|
||||
{
|
||||
|
||||
if (!check_permission('pesan', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$table = 'notifikasi';
|
||||
$column_order = array(null, 'created_at', 'type', 'nomor_whatsapp', 'email', 'pesan', 'status');
|
||||
$column_search = array('pesan', 'nomor_whatsapp', 'email', 'status', 'type');
|
||||
$order = array('created_at' => 'DESC');
|
||||
|
||||
$this->db->from($table);
|
||||
|
||||
$search_value = !empty($_POST['search']['value']) ? $_POST['search']['value'] : '';
|
||||
|
||||
// SEARCH
|
||||
if ($search_value != '') {
|
||||
$this->db->group_start();
|
||||
foreach ($column_search as $i => $col_name) {
|
||||
if ($i === 0) {
|
||||
$this->db->like($col_name, $search_value);
|
||||
} else {
|
||||
$this->db->or_like($col_name, $search_value);
|
||||
}
|
||||
}
|
||||
$this->db->group_end();
|
||||
}
|
||||
|
||||
// ORDER
|
||||
if (isset($_POST['order'])) {
|
||||
$col_index = $_POST['order'][0]['column'];
|
||||
$dir = $_POST['order'][0]['dir'];
|
||||
if (isset($column_order[$col_index])) {
|
||||
$this->db->order_by($column_order[$col_index], $dir);
|
||||
}
|
||||
} else {
|
||||
$this->db->order_by(key($order), $order[key($order)]);
|
||||
}
|
||||
|
||||
// PAGINATION
|
||||
if ($_POST['length'] != -1) {
|
||||
$this->db->limit($_POST['length'], $_POST['start']);
|
||||
}
|
||||
|
||||
$query = $this->db->get();
|
||||
$data = [];
|
||||
$no = $_POST['start'];
|
||||
|
||||
foreach ($query->result() as $row) {
|
||||
$no++;
|
||||
|
||||
// Badge for 'status'
|
||||
$statusBadge = '';
|
||||
switch ($row->status) {
|
||||
case 'terkirim': $statusBadge = '<span class="badge bg-success">Terkirim</span>'; break;
|
||||
case 'proses': $statusBadge = '<span class="badge bg-info">Proses</span>'; break;
|
||||
case 'pending': $statusBadge = '<span class="badge bg-warning">Pending</span>'; break;
|
||||
case 'dibaca': $statusBadge = '<span class="badge bg-primary">Dibaca</span>'; break;
|
||||
default: $statusBadge = '<span class="badge bg-secondary">' . htmlspecialchars($row->status) . '</span>'; break;
|
||||
}
|
||||
|
||||
// Type display
|
||||
$typeDisplay = '';
|
||||
switch ($row->type) {
|
||||
case 'wa_official': $typeDisplay = 'WhatsApp Official'; break;
|
||||
case 'wa_unofficial': $typeDisplay = 'WhatsApp Unofficial'; break;
|
||||
case 'email': $typeDisplay = 'Email'; break;
|
||||
default: $typeDisplay = htmlspecialchars($row->type); break;
|
||||
}
|
||||
|
||||
$penerima = !empty($row->nomor_whatsapp) ? htmlspecialchars($row->nomor_whatsapp) : htmlspecialchars($row->email);
|
||||
|
||||
$data[] = [
|
||||
$no,
|
||||
date('d-m-Y H:i:s', strtotime($row->created_at)),
|
||||
$typeDisplay,
|
||||
$penerima,
|
||||
'<div class="truncate-text">' . htmlspecialchars($row->pesan) . '</div>',
|
||||
$statusBadge
|
||||
];
|
||||
}
|
||||
|
||||
// COUNT TOTAL RECORDS
|
||||
$this->db->from($table);
|
||||
if ($search_value != '') {
|
||||
$this->db->group_start();
|
||||
foreach ($column_search as $i => $col_name) {
|
||||
if ($i === 0) {
|
||||
$this->db->like($col_name, $search_value);
|
||||
} else {
|
||||
$this->db->or_like($col_name, $search_value);
|
||||
}
|
||||
}
|
||||
$this->db->group_end();
|
||||
}
|
||||
$recordsFiltered = $this->db->count_all_results();
|
||||
|
||||
$output = [
|
||||
"draw" => intval($_POST['draw']),
|
||||
"recordsTotal" => $this->db->count_all($table),
|
||||
"recordsFiltered" => $recordsFiltered,
|
||||
"data" => $data
|
||||
];
|
||||
|
||||
echo json_encode($output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Qr extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// Load library QR Code manual
|
||||
require_once APPPATH . 'libraries/phpqrcode/qrlib.php';
|
||||
|
||||
// Cek apakah user sudah login
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('login');
|
||||
}
|
||||
}
|
||||
|
||||
// Tampilkan QR (dengan margin kecil)
|
||||
public function show($text = "contoh")
|
||||
{
|
||||
header("Content-Type: image/png");
|
||||
|
||||
// Param: text, outfile, ECC, size, margin
|
||||
QRcode::png($text, null, QR_ECLEVEL_H, 8, 1);
|
||||
}
|
||||
|
||||
// Download QR Code
|
||||
public function download($text = "contoh")
|
||||
{
|
||||
$fileName = "QR_" . time() . ".png";
|
||||
$tempDir = FCPATH . "downloads/";
|
||||
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0777, true);
|
||||
}
|
||||
|
||||
$filePath = $tempDir . $fileName;
|
||||
|
||||
// Generate QR ke file (margin diperkecil)
|
||||
QRcode::png($text, $filePath, QR_ECLEVEL_H, 8, 1);
|
||||
|
||||
// Force download
|
||||
header('Content-Type: image/png');
|
||||
header('Content-Disposition: attachment; filename="'.$fileName.'"');
|
||||
readfile($filePath);
|
||||
|
||||
// Hapus setelah download
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Scheduler extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// PROCESS ATTENDANCE LOG
|
||||
// =====================================================
|
||||
public function process_attendance()
|
||||
{
|
||||
echo "PROCESS ATTENDANCE START\n";
|
||||
|
||||
// =============================================
|
||||
// GET PENDING LOG
|
||||
// =============================================
|
||||
$logs = $this->db
|
||||
->where('process_status','pending')
|
||||
->order_by('check_time','ASC')
|
||||
->limit(1000)
|
||||
->get('k_attendance_logs')
|
||||
->result();
|
||||
|
||||
foreach($logs as $log){
|
||||
|
||||
try{
|
||||
|
||||
// =====================================
|
||||
// GET EMPLOYEE
|
||||
// =====================================
|
||||
$employee = $this->db
|
||||
->where(
|
||||
'fingerprint_user_id',
|
||||
$log->user_id
|
||||
)
|
||||
->where('is_active',1)
|
||||
->get('k_employees')
|
||||
->row();
|
||||
|
||||
if(!$employee){
|
||||
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'failed'
|
||||
);
|
||||
|
||||
echo "FAILED EMPLOYEE : {$log->user_id}\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =====================================
|
||||
// UPDATE EMPLOYEE ID
|
||||
// =====================================
|
||||
$this->db
|
||||
->where('id',$log->id)
|
||||
->update(
|
||||
'k_attendance_logs',
|
||||
[
|
||||
'employee_id' => $employee->id
|
||||
]
|
||||
);
|
||||
|
||||
$logDateTime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime($log->check_time)
|
||||
);
|
||||
|
||||
// =====================================
|
||||
// FIND SCHEDULE
|
||||
// SUPPORT SHIFT MALAM
|
||||
// =====================================
|
||||
$this->db->from(
|
||||
'k_employee_shift_schedules'
|
||||
);
|
||||
|
||||
$this->db->where(
|
||||
'employee_id',
|
||||
$employee->id
|
||||
);
|
||||
|
||||
$this->db->where(
|
||||
'status',
|
||||
'published'
|
||||
);
|
||||
|
||||
// =====================================
|
||||
// RANGE SHIFT
|
||||
// =====================================
|
||||
$this->db->where("
|
||||
'{$logDateTime}' BETWEEN
|
||||
DATE_SUB(start_datetime, INTERVAL 3 HOUR)
|
||||
AND
|
||||
DATE_ADD(end_datetime, INTERVAL 6 HOUR)
|
||||
", null, false);
|
||||
|
||||
$this->db->order_by(
|
||||
'start_datetime',
|
||||
'DESC'
|
||||
);
|
||||
|
||||
$schedule = $this->db->get()->row();
|
||||
|
||||
// =====================================
|
||||
// SCHEDULE NOT FOUND
|
||||
// =====================================
|
||||
if(!$schedule){
|
||||
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'failed'
|
||||
);
|
||||
|
||||
echo "FAILED SCHEDULE : {$logDateTime}\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =====================================
|
||||
// GET SHIFT
|
||||
// =====================================
|
||||
$shift = $this->db
|
||||
->where('id',$schedule->shift_id)
|
||||
->get('k_shifts')
|
||||
->row();
|
||||
|
||||
if(!$shift){
|
||||
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'failed'
|
||||
);
|
||||
|
||||
echo "FAILED SHIFT : {$schedule->shift_id}\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =====================================
|
||||
// ATTENDANCE DATE
|
||||
// =====================================
|
||||
$attendance_date = date(
|
||||
'Y-m-d',
|
||||
strtotime($schedule->start_datetime)
|
||||
);
|
||||
|
||||
// =====================================
|
||||
// GET ATTENDANCE
|
||||
// =====================================
|
||||
$attendance = $this->db
|
||||
->where(
|
||||
'employee_id',
|
||||
$employee->id
|
||||
)
|
||||
->where(
|
||||
'attendance_date',
|
||||
$attendance_date
|
||||
)
|
||||
->get('k_attendances')
|
||||
->row();
|
||||
|
||||
// =====================================
|
||||
// CHECK LATE
|
||||
// =====================================
|
||||
$late_minutes = 0;
|
||||
|
||||
$maxCheckin = strtotime(
|
||||
$schedule->start_datetime
|
||||
) + (
|
||||
$shift->late_tolerance_minutes * 60
|
||||
);
|
||||
|
||||
if(
|
||||
strtotime($logDateTime)
|
||||
>
|
||||
$maxCheckin
|
||||
){
|
||||
|
||||
$late_minutes = floor(
|
||||
(
|
||||
strtotime($logDateTime)
|
||||
-
|
||||
strtotime($schedule->start_datetime)
|
||||
) / 60
|
||||
);
|
||||
|
||||
if($late_minutes < 0){
|
||||
$late_minutes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================
|
||||
// INSERT CHECKIN
|
||||
// =====================================
|
||||
if(!$attendance){
|
||||
|
||||
$this->db->insert(
|
||||
'k_attendances',
|
||||
[
|
||||
|
||||
'employee_id' => $employee->id,
|
||||
|
||||
'attendance_date' => $attendance_date,
|
||||
|
||||
'checkin_time' => $logDateTime,
|
||||
|
||||
'late_minutes' => $late_minutes,
|
||||
|
||||
'attendance_status' => (
|
||||
$late_minutes > 0
|
||||
? 'late'
|
||||
: 'present'
|
||||
),
|
||||
|
||||
'shift_id' => $shift->id,
|
||||
|
||||
'attendance_log_in_id' => $log->id,
|
||||
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
]
|
||||
);
|
||||
|
||||
echo "CHECKIN : {$employee->full_name}\n";
|
||||
|
||||
} else {
|
||||
|
||||
// =================================
|
||||
// SUDAH ADA CHECKOUT
|
||||
// =================================
|
||||
if(!empty($attendance->checkout_time)){
|
||||
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'processed'
|
||||
);
|
||||
|
||||
echo "SKIP LOG (SUDAH CHECKOUT)\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =================================
|
||||
// CEK SELISIH JAM
|
||||
// =================================
|
||||
$diffMinutes = floor(
|
||||
(
|
||||
strtotime($logDateTime)
|
||||
-
|
||||
strtotime($attendance->checkin_time)
|
||||
) / 60
|
||||
);
|
||||
|
||||
// =================================
|
||||
// MINIMAL 2 JAM
|
||||
// BARU DIANGGAP CHECKOUT
|
||||
// =================================
|
||||
if($diffMinutes < 120){
|
||||
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'processed'
|
||||
);
|
||||
|
||||
echo "SKIP DUPLICATE LOG : {$employee->full_name}\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =================================
|
||||
// WORK HOURS
|
||||
// =================================
|
||||
$workHours = (
|
||||
strtotime($logDateTime)
|
||||
-
|
||||
strtotime($attendance->checkin_time)
|
||||
) / 3600;
|
||||
|
||||
if($workHours < 0){
|
||||
$workHours = 0;
|
||||
}
|
||||
|
||||
// =================================
|
||||
// EARLY LEAVE
|
||||
// =================================
|
||||
$early_leave_minutes = 0;
|
||||
|
||||
if(
|
||||
strtotime($logDateTime)
|
||||
<
|
||||
strtotime($schedule->end_datetime)
|
||||
){
|
||||
|
||||
$early_leave_minutes = floor(
|
||||
(
|
||||
strtotime($schedule->end_datetime)
|
||||
-
|
||||
strtotime($logDateTime)
|
||||
) / 60
|
||||
);
|
||||
|
||||
if($early_leave_minutes < 0){
|
||||
$early_leave_minutes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =================================
|
||||
// OVERTIME
|
||||
// =================================
|
||||
$overtime_hours = 0;
|
||||
|
||||
if(
|
||||
strtotime($logDateTime)
|
||||
>
|
||||
strtotime($schedule->end_datetime)
|
||||
){
|
||||
|
||||
$overtime_hours = round(
|
||||
(
|
||||
strtotime($logDateTime)
|
||||
-
|
||||
strtotime($schedule->end_datetime)
|
||||
) / 3600,
|
||||
2
|
||||
);
|
||||
|
||||
if($overtime_hours < 0){
|
||||
$overtime_hours = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =================================
|
||||
// UPDATE CHECKOUT
|
||||
// =================================
|
||||
$this->db
|
||||
->where(
|
||||
'id',
|
||||
$attendance->id
|
||||
)
|
||||
->update(
|
||||
'k_attendances',
|
||||
[
|
||||
|
||||
'checkout_time' => $logDateTime,
|
||||
|
||||
'attendance_log_out_id' => $log->id,
|
||||
|
||||
'work_hours' => round($workHours,2),
|
||||
|
||||
'early_leave_minutes' => $early_leave_minutes,
|
||||
|
||||
'overtime_hours' => $overtime_hours
|
||||
]
|
||||
);
|
||||
|
||||
echo "CHECKOUT : {$employee->full_name}\n";
|
||||
}
|
||||
|
||||
// =====================================
|
||||
// UPDATE LOG STATUS
|
||||
// =====================================
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'processed'
|
||||
);
|
||||
|
||||
echo "SUCCESS LOG ID : {$log->id}\n";
|
||||
|
||||
} catch(Exception $e){
|
||||
|
||||
$this->updateLogStatus(
|
||||
$log->id,
|
||||
'failed'
|
||||
);
|
||||
|
||||
echo "ERROR : " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "PROCESS ATTENDANCE DONE\n";
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// AUTO CLOSE ATTENDANCE
|
||||
// =====================================================
|
||||
public function auto_close_attendance()
|
||||
{
|
||||
echo "AUTO CLOSE START\n";
|
||||
|
||||
// =============================================
|
||||
// AMBIL ATTENDANCE BELUM CHECKOUT
|
||||
// =============================================
|
||||
$attendances = $this->db
|
||||
->where('checkout_time IS NULL', null, false)
|
||||
->get('k_attendances')
|
||||
->result();
|
||||
|
||||
foreach($attendances as $attendance){
|
||||
|
||||
// =========================================
|
||||
// GET SCHEDULE
|
||||
// =========================================
|
||||
$schedule = $this->db
|
||||
->where(
|
||||
'employee_id',
|
||||
$attendance->employee_id
|
||||
)
|
||||
->where(
|
||||
'shift_id',
|
||||
$attendance->shift_id
|
||||
)
|
||||
->where(
|
||||
'DATE(start_datetime)',
|
||||
$attendance->attendance_date
|
||||
)
|
||||
->get('k_employee_shift_schedules')
|
||||
->row();
|
||||
|
||||
if(!$schedule){
|
||||
continue;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// AUTO CLOSE
|
||||
// END SHIFT + 4 JAM
|
||||
// =========================================
|
||||
$autoCloseTime = date(
|
||||
'Y-m-d H:i:s',
|
||||
strtotime(
|
||||
$schedule->end_datetime . ' +4 hours'
|
||||
)
|
||||
);
|
||||
|
||||
// =========================================
|
||||
// BELUM WAKTUNYA
|
||||
// =========================================
|
||||
if(
|
||||
strtotime(date('Y-m-d H:i:s'))
|
||||
<
|
||||
strtotime($autoCloseTime)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// WORK HOURS
|
||||
// =========================================
|
||||
$workHours = (
|
||||
strtotime($schedule->end_datetime)
|
||||
-
|
||||
strtotime($attendance->checkin_time)
|
||||
) / 3600;
|
||||
|
||||
if($workHours < 0){
|
||||
$workHours = 0;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// AUTO CHECKOUT
|
||||
// =========================================
|
||||
$this->db
|
||||
->where('id',$attendance->id)
|
||||
->update(
|
||||
'k_attendances',
|
||||
[
|
||||
|
||||
'checkout_time' => $schedule->end_datetime,
|
||||
|
||||
'work_hours' => round($workHours,2),
|
||||
|
||||
'notes' => 'Auto checkout by system'
|
||||
]
|
||||
);
|
||||
|
||||
echo "AUTO CLOSE EMPLOYEE : {$attendance->employee_id}\n";
|
||||
}
|
||||
|
||||
echo "AUTO CLOSE DONE\n";
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// GENERATE DAILY ATTENDANCE
|
||||
// =====================================================
|
||||
public function generate_daily_attendance()
|
||||
{
|
||||
echo "GENERATE DAILY START\n";
|
||||
|
||||
// =============================================
|
||||
// GENERATE HARI KEMARIN
|
||||
// =============================================
|
||||
$date = date(
|
||||
'Y-m-d',
|
||||
strtotime('-1 day')
|
||||
);
|
||||
|
||||
// =============================================
|
||||
// GET ALL SCHEDULE
|
||||
// =============================================
|
||||
$schedules = $this->db
|
||||
->where('schedule_date', $date)
|
||||
->where('status','published')
|
||||
->get('k_employee_shift_schedules')
|
||||
->result();
|
||||
|
||||
foreach($schedules as $schedule){
|
||||
|
||||
// =========================================
|
||||
// CHECK ATTENDANCE EXIST
|
||||
// =========================================
|
||||
$attendance = $this->db
|
||||
->where(
|
||||
'employee_id',
|
||||
$schedule->employee_id
|
||||
)
|
||||
->where(
|
||||
'attendance_date',
|
||||
$date
|
||||
)
|
||||
->get('k_attendances')
|
||||
->row();
|
||||
|
||||
if($attendance){
|
||||
continue;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// CHECK HOLIDAY
|
||||
// =========================================
|
||||
$holiday = $this->db
|
||||
->where('holiday_date',$date)
|
||||
->get('k_holidays')
|
||||
->row();
|
||||
|
||||
if($holiday){
|
||||
|
||||
$this->db->insert(
|
||||
'k_attendances',
|
||||
[
|
||||
|
||||
'employee_id' => $schedule->employee_id,
|
||||
|
||||
'attendance_date' => $date,
|
||||
|
||||
'attendance_status' => 'holiday',
|
||||
|
||||
'is_holiday' => 1,
|
||||
|
||||
'shift_id' => $schedule->shift_id,
|
||||
|
||||
'notes' => $holiday->holiday_name
|
||||
]
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// CHECK LEAVE
|
||||
// =========================================
|
||||
$leave = $this->db
|
||||
->where(
|
||||
'employee_id',
|
||||
$schedule->employee_id
|
||||
)
|
||||
->where(
|
||||
'approval_status',
|
||||
'approved'
|
||||
)
|
||||
->where('start_date <=',$date)
|
||||
->where('end_date >=',$date)
|
||||
->get('k_leave_requests')
|
||||
->row();
|
||||
|
||||
if($leave){
|
||||
|
||||
$this->db->insert(
|
||||
'k_attendances',
|
||||
[
|
||||
|
||||
'employee_id' => $schedule->employee_id,
|
||||
|
||||
'attendance_date' => $date,
|
||||
|
||||
'attendance_status' => $leave->leave_type,
|
||||
|
||||
'shift_id' => $schedule->shift_id,
|
||||
|
||||
'notes' => $leave->reason
|
||||
]
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// DEFAULT ALPHA
|
||||
// =========================================
|
||||
$this->db->insert(
|
||||
'k_attendances',
|
||||
[
|
||||
|
||||
'employee_id' => $schedule->employee_id,
|
||||
|
||||
'attendance_date' => $date,
|
||||
|
||||
'attendance_status' => 'alpha',
|
||||
|
||||
'shift_id' => $schedule->shift_id,
|
||||
|
||||
'notes' => 'Tidak hadir'
|
||||
]
|
||||
);
|
||||
|
||||
echo "GENERATE EMPLOYEE : {$schedule->employee_id}\n";
|
||||
}
|
||||
|
||||
echo "GENERATE DAILY DONE\n";
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// UPDATE LOG STATUS
|
||||
// =====================================================
|
||||
private function updateLogStatus(
|
||||
$id,
|
||||
$status
|
||||
){
|
||||
|
||||
$this->db
|
||||
->where('id',$id)
|
||||
->update(
|
||||
'k_attendance_logs',
|
||||
[
|
||||
'process_status' => $status
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Setting extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// Cek apakah user sudah login
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('login');
|
||||
}
|
||||
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Anda tidak memiliki izin untuk mengakses halaman ini.', 403);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// MAIN PAGE
|
||||
// ==========================
|
||||
public function index()
|
||||
{
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
// Ambil config id=1
|
||||
$cfg = $this->db->get_where("config", ["id" => 1])->row();
|
||||
|
||||
// Jika tidak ada data config
|
||||
$config = $cfg ?: $this->defaultConfig();
|
||||
|
||||
// Ambil template pesan
|
||||
$template_pesan = $this->db
|
||||
->order_by("id", "ASC")
|
||||
->get("template_pesan")
|
||||
->result();
|
||||
|
||||
$data = [
|
||||
"active_menu" => "pengaturan",
|
||||
"config" => $config,
|
||||
"template_pesan" => $template_pesan
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('setting/layout', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// DEFAULT CONFIG
|
||||
// ===============================
|
||||
private function defaultConfig()
|
||||
{
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
return (object)[
|
||||
"nama_lembaga" => "",
|
||||
"pimpinan_lembaga" => "",
|
||||
"card_background" => "",
|
||||
"card_orientation" => "landscape",
|
||||
"wa_mode" => "official",
|
||||
"wa_phone" => "",
|
||||
"wa_phone_id" => "",
|
||||
"wa_waba_id" => "",
|
||||
"wa_access_token" => "",
|
||||
"wa_endpoint" => "",
|
||||
"wa_api_key" => ""
|
||||
];
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 1. SIMPAN SETTING UMUM
|
||||
// ===============================
|
||||
public function save_umum()
|
||||
{
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$update = [
|
||||
"nama_lembaga" => $this->input->post("nama_lembaga"),
|
||||
"pimpinan_lembaga" => $this->input->post("pimpinan_lembaga"),
|
||||
"updated_at" => date("Y-m-d H:i:s")
|
||||
];
|
||||
|
||||
$this->db->where("id", 1)->update("config", $update);
|
||||
|
||||
redirect("setting");
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 2. SIMPAN CARD + UPLOAD BG
|
||||
// ===============================
|
||||
public function save_card()
|
||||
{
|
||||
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$orientation = $this->input->post("card_orientation");
|
||||
$bg = null;
|
||||
|
||||
// Jika ada upload file
|
||||
if (!empty($_FILES["card_background"]["name"])) {
|
||||
|
||||
$config['upload_path'] = './uploads/card/';
|
||||
$config['allowed_types'] = 'jpg|jpeg|png';
|
||||
$config['file_name'] = 'background_' . time();
|
||||
|
||||
if (!is_dir('./uploads/card/')) {
|
||||
mkdir('./uploads/card/', 0777, true);
|
||||
}
|
||||
|
||||
$this->load->library('upload', $config);
|
||||
|
||||
if ($this->upload->do_upload('card_background')) {
|
||||
$file = $this->upload->data();
|
||||
$bg = "uploads/card/" . $file['file_name'];
|
||||
}
|
||||
}
|
||||
|
||||
// Data update
|
||||
$data = [
|
||||
"card_orientation" => $orientation,
|
||||
"updated_at" => date("Y-m-d H:i:s")
|
||||
];
|
||||
|
||||
// Jika ada gambar baru
|
||||
if ($bg !== null) {
|
||||
$data["card_background"] = $bg;
|
||||
}
|
||||
|
||||
$this->db->where("id", 1)->update("config", $data);
|
||||
|
||||
redirect("setting");
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 3. SIMPAN SETTING WHATSAPP
|
||||
// ===============================
|
||||
public function save_whatsapp()
|
||||
{
|
||||
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$mode = $this->input->post("wa_mode");
|
||||
|
||||
$data = [
|
||||
"wa_mode" => $mode,
|
||||
"updated_at" => date("Y-m-d H:i:s")
|
||||
];
|
||||
|
||||
if ($mode == "official") {
|
||||
|
||||
$data["wa_phone"] = $this->input->post("wa_phone");
|
||||
$data["wa_phone_id"] = $this->input->post("wa_phone_id");
|
||||
$data["wa_waba_id"] = $this->input->post("wa_waba_id");
|
||||
$data["wa_access_token"] = $this->input->post("wa_access_token");
|
||||
|
||||
// Kosongkan unofficial
|
||||
$data["wa_endpoint"] = "";
|
||||
$data["wa_api_key"] = "";
|
||||
|
||||
} else {
|
||||
|
||||
$data["wa_endpoint"] = $this->input->post("wa_endpoint");
|
||||
$data["wa_api_key"] = $this->input->post("wa_api_key");
|
||||
|
||||
// Kosongkan official
|
||||
$data["wa_phone"] = "";
|
||||
$data["wa_phone_id"] = "";
|
||||
$data["wa_waba_id"] = "";
|
||||
$data["wa_access_token"] = "";
|
||||
}
|
||||
|
||||
$this->db->where("id", 1)->update("config", $data);
|
||||
|
||||
redirect("setting");
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 4. UPDATE TEMPLATE PESAN
|
||||
// ===============================
|
||||
public function update_template_pesan()
|
||||
{
|
||||
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$data = [
|
||||
"nama_template" => $this->input->post("nama_template"),
|
||||
"isi_template" => $this->input->post("isi_template")
|
||||
];
|
||||
|
||||
$id = $this->input->post("id");
|
||||
|
||||
$this->db->where("id", $id)->update("template_pesan", $data);
|
||||
|
||||
redirect("setting");
|
||||
}
|
||||
|
||||
public function cek_whatsapp_status()
|
||||
{
|
||||
|
||||
if (!check_permission('setting', 'can_view')) {
|
||||
show_error('Tidak memiliki izin untuk melihat detail.', 403);
|
||||
}
|
||||
|
||||
$config = $this->db->get_where("config", ["id" => 1])->row();
|
||||
|
||||
if (!$config) {
|
||||
echo json_encode([
|
||||
"status" => "error",
|
||||
"img" => base_url("uploads/whatsapp/disconnect.png"),
|
||||
"msg" => "Config tidak ditemukan"
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fungsi bantu call API
|
||||
function callCurl($url, $fields)
|
||||
{
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 0,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
CURLOPT_POSTFIELDS => $fields,
|
||||
CURLOPT_HTTPHEADER => array( 'Content-Type: application/x-www-form-urlencoded' ),
|
||||
]);
|
||||
$response = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 1. CEK STATUS
|
||||
$response = callCurl(
|
||||
$config->wa_endpoint . "getState",
|
||||
"apiKey=" . $config->wa_api_key
|
||||
);
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (!$data || !isset($data['results']['state'])) {
|
||||
echo json_encode([
|
||||
"status" => "error",
|
||||
"img" => base_url('uploads/whatsapp/disconnect.png'),
|
||||
"msg" => "Gagal membaca respon server WA",
|
||||
"data" => $data,
|
||||
"url_key" => $config->wa_endpoint . ' ' . $config->wa_api_key
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$state = $data['results']['state'];
|
||||
|
||||
// ==========================================================
|
||||
// 1. CONNECTED → tampilkan gambar connected
|
||||
// ==========================================================
|
||||
if ($state == "CONNECTED") {
|
||||
echo json_encode([
|
||||
"status" => "CONNECTED",
|
||||
"img" => base_url('uploads/whatsapp/connect.png')
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ==========================================================
|
||||
// 2. SERVICE_OFF → tampilkan disconnect + mulai service
|
||||
// ==========================================================
|
||||
if ($state == "SERVICE_OFF") {
|
||||
|
||||
// Mulai service
|
||||
callCurl(
|
||||
$config->wa_endpoint . "serviceStart",
|
||||
"apiKey=" . $config->wa_api_key
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
"status" => "SERVICE_OFF",
|
||||
"img" => base_url('uploads/whatsapp/disconnect.png')
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ==========================================================
|
||||
// 3. SERVICE_SCAN → ambil QR
|
||||
// ==========================================================
|
||||
if ($state == "SERVICE_SCAN") {
|
||||
|
||||
$responseQR = callCurl(
|
||||
$config->wa_endpoint . "getQR",
|
||||
"apiKey=" . $config->wa_api_key
|
||||
);
|
||||
|
||||
$qrData = json_decode($responseQR, true);
|
||||
|
||||
if (!isset($qrData['results']['qrString'])) {
|
||||
echo json_encode([
|
||||
"status" => "SERVICE_SCAN",
|
||||
"img" => base_url('uploads/whatsapp/map_loader.gif')
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"status" => "SERVICE_SCAN",
|
||||
"img" => $qrData['results']['qrString']
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// ==========================================================
|
||||
// 4. STATUS LAIN → tampilkan loader
|
||||
// ==========================================================
|
||||
echo json_encode([
|
||||
"status" => "UNKNOWN",
|
||||
"img" => base_url('uploads/whatsapp/map_loader.gif')
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Shifts extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// PAGE
|
||||
// ==========================================
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "shift"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/shift');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// DATATABLE
|
||||
// =====================================================
|
||||
public function get_data()
|
||||
{
|
||||
$list = $this->db
|
||||
->order_by('id','DESC')
|
||||
->get('k_shifts')
|
||||
->result();
|
||||
|
||||
$data = [];
|
||||
$no = 1;
|
||||
|
||||
foreach($list as $r){
|
||||
|
||||
$night = $r->is_night_shift == 1
|
||||
? '<span class="badge bg-dark">Night Shift</span>'
|
||||
: '<span class="badge bg-success">Normal</span>';
|
||||
|
||||
$data[] = [
|
||||
$no++,
|
||||
$r->shift_code,
|
||||
$r->shift_name,
|
||||
$r->checkin_time,
|
||||
$r->checkout_time,
|
||||
$r->late_tolerance_minutes . ' Menit',
|
||||
$r->work_hours . ' Jam',
|
||||
$night,
|
||||
'
|
||||
<button class="btn btn-warning btn-sm btn-edit"
|
||||
data-id="'.$r->id.'">
|
||||
Edit
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger btn-sm btn-delete"
|
||||
data-id="'.$r->id.'">
|
||||
Delete
|
||||
</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"data" => $data
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// DETAIL
|
||||
// =====================================================
|
||||
public function detail($id)
|
||||
{
|
||||
$data = $this->db
|
||||
->get_where('k_shifts',['id'=>$id])
|
||||
->row();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// SAVE
|
||||
// =====================================================
|
||||
public function save()
|
||||
{
|
||||
$data = [
|
||||
'shift_code' => $this->input->post('shift_code'),
|
||||
'shift_name' => $this->input->post('shift_name'),
|
||||
'checkin_time' => $this->input->post('checkin_time'),
|
||||
'checkout_time' => $this->input->post('checkout_time'),
|
||||
'late_tolerance_minutes' => $this->input->post('late_tolerance_minutes'),
|
||||
'work_hours' => $this->input->post('work_hours'),
|
||||
'is_night_shift' => $this->input->post('is_night_shift')
|
||||
];
|
||||
|
||||
$this->db->insert('k_shifts',$data);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message'=> 'Shift berhasil ditambahkan'
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// UPDATE
|
||||
// =====================================================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
|
||||
$data = [
|
||||
'shift_code' => $this->input->post('shift_code'),
|
||||
'shift_name' => $this->input->post('shift_name'),
|
||||
'checkin_time' => $this->input->post('checkin_time'),
|
||||
'checkout_time' => $this->input->post('checkout_time'),
|
||||
'late_tolerance_minutes' => $this->input->post('late_tolerance_minutes'),
|
||||
'work_hours' => $this->input->post('work_hours'),
|
||||
'is_night_shift' => $this->input->post('is_night_shift')
|
||||
];
|
||||
|
||||
$this->db->where('id',$id);
|
||||
$this->db->update('k_shifts',$data);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message'=> 'Shift berhasil diupdate'
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// DELETE
|
||||
// =====================================================
|
||||
public function delete($id)
|
||||
{
|
||||
$this->db->delete('k_shifts',['id'=>$id]);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message'=> 'Shift berhasil dihapus'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Users extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
$this->load->library('form_validation');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "users"];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('users/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DATATABLE USERS + ROLES
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$this->db->select('
|
||||
users.id,
|
||||
users.username,
|
||||
users.nama,
|
||||
roles.nama_role
|
||||
');
|
||||
$this->db->from('users');
|
||||
$this->db->join('roles', 'roles.id = users.role_id', 'left');
|
||||
|
||||
$column_order = ['users.id', 'users.username', 'users.nama', 'roles.nama_role'];
|
||||
$column_search = ['users.username', 'users.nama', 'roles.nama_role'];
|
||||
$order = ['users.id' => 'DESC'];
|
||||
|
||||
$search_value = $this->input->post('search')['value'] ?? '';
|
||||
|
||||
if (!empty($search_value)) {
|
||||
$this->db->group_start();
|
||||
foreach ($column_search as $i => $col) {
|
||||
if ($i === 0) {
|
||||
$this->db->like($col, $search_value);
|
||||
} else {
|
||||
$this->db->or_like($col, $search_value);
|
||||
}
|
||||
}
|
||||
$this->db->group_end();
|
||||
}
|
||||
|
||||
if ($this->input->post('order')) {
|
||||
$col_index = $this->input->post('order')[0]['column'];
|
||||
$dir = $this->input->post('order')[0]['dir'];
|
||||
|
||||
if (isset($column_order[$col_index])) {
|
||||
$this->db->order_by($column_order[$col_index], $dir);
|
||||
}
|
||||
} else {
|
||||
$this->db->order_by(key($order), $order[key($order)]);
|
||||
}
|
||||
|
||||
if ($this->input->post('length') != -1) {
|
||||
$this->db->limit(
|
||||
$this->input->post('length'),
|
||||
$this->input->post('start')
|
||||
);
|
||||
}
|
||||
|
||||
$query = $this->db->get();
|
||||
$data = $query->result();
|
||||
|
||||
$output = [
|
||||
"draw" => intval($this->input->post('draw')),
|
||||
"recordsTotal" => $this->db->count_all('users'),
|
||||
"recordsFiltered" => $this->_count_filtered($search_value),
|
||||
"data" => []
|
||||
];
|
||||
|
||||
foreach ($data as $row) {
|
||||
|
||||
$action = '
|
||||
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Hapus</button>
|
||||
';
|
||||
|
||||
$output['data'][] = [
|
||||
$row->id,
|
||||
$row->username,
|
||||
$row->nama,
|
||||
$row->nama_role,
|
||||
$action
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($output);
|
||||
}
|
||||
|
||||
private function _count_filtered($search_value)
|
||||
{
|
||||
$this->db->from('users');
|
||||
$this->db->join('roles', 'roles.id = users.role_id', 'left');
|
||||
|
||||
if (!empty($search_value)) {
|
||||
$this->db->group_start();
|
||||
$this->db->like('users.username', $search_value);
|
||||
$this->db->or_like('users.nama', $search_value);
|
||||
$this->db->or_like('roles.nama_role', $search_value);
|
||||
$this->db->group_end();
|
||||
}
|
||||
|
||||
return $this->db->count_all_results();
|
||||
}
|
||||
|
||||
// =========================
|
||||
// STORE USER
|
||||
// =========================
|
||||
public function store()
|
||||
{
|
||||
$this->form_validation->set_rules('username', 'Username', 'required|is_unique[users.username]');
|
||||
$this->form_validation->set_rules('nama', 'Nama', 'required');
|
||||
$this->form_validation->set_rules('role_id', 'Role', 'required');
|
||||
$this->form_validation->set_rules('password', 'Password', 'required|min_length[6]');
|
||||
|
||||
if ($this->form_validation->run() == FALSE) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => validation_errors()
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'username' => $this->input->post('username'),
|
||||
'nama' => $this->input->post('nama'),
|
||||
'role_id' => $this->input->post('role_id'),
|
||||
'password' => password_hash($this->input->post('password'), PASSWORD_DEFAULT)
|
||||
];
|
||||
|
||||
$this->dm->setTable('users')->insert($data);
|
||||
|
||||
log_activity(
|
||||
'users',
|
||||
'create',
|
||||
'Menambahkan user: ' . $data['username'],
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'User berhasil ditambahkan'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// EDIT USER
|
||||
// =========================
|
||||
public function edit($id)
|
||||
{
|
||||
$user = $this->db->get_where('users', ['id' => $id])->row();
|
||||
|
||||
if ($user) {
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'data' => $user
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'User tidak ditemukan'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// UPDATE USER
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
|
||||
$this->form_validation->set_rules('username', 'Username', 'required');
|
||||
$this->form_validation->set_rules('nama', 'Nama', 'required');
|
||||
$this->form_validation->set_rules('role_id', 'Role', 'required');
|
||||
|
||||
if (!empty($this->input->post('password'))) {
|
||||
$this->form_validation->set_rules('password', 'Password', 'min_length[6]');
|
||||
}
|
||||
|
||||
if ($this->form_validation->run() == FALSE) {
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => validation_errors()
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'username' => $this->input->post('username'),
|
||||
'nama' => $this->input->post('nama'),
|
||||
'role_id' => $this->input->post('role_id')
|
||||
];
|
||||
|
||||
if (!empty($this->input->post('password'))) {
|
||||
$data['password'] = password_hash($this->input->post('password'), PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
$this->dm->setTable('users')->update($id, $data);
|
||||
|
||||
log_activity(
|
||||
'users',
|
||||
'update',
|
||||
'Update user ID: ' . $id . ' username: ' . $this->input->post('username'),
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'User berhasil diupdate'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE USER
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
$this->dm->setTable('users')->delete($id);
|
||||
|
||||
log_activity(
|
||||
'users',
|
||||
'delete',
|
||||
'Hapus user ID: ' . $id,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'User berhasil dihapus'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET ROLES
|
||||
// =========================
|
||||
public function get_role()
|
||||
{
|
||||
$roles = $this->db->get('roles')->result();
|
||||
|
||||
echo json_encode($roles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Warehouses extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = ["active_menu" => "warehouses"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('warehouses/layout');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA (DATATABLE)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$data = $this->db->get('warehouses')->result();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$result[] = [
|
||||
$no++,
|
||||
$row->nama,
|
||||
'
|
||||
<button class="btn btn-sm btn-secondary btn-editgudang" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(["data"=>$result]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DETAIL
|
||||
// =========================
|
||||
public function detail($id)
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get_where('warehouses',['id'=>$id])->row()
|
||||
);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// SAVE
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$nama = $this->input->post('nama');
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->insert('warehouses', [
|
||||
'nama' => $nama
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'create',
|
||||
'Tambah gudang '.$nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// UPDATE
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$nama = $this->input->post('nama');
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->where('id',$id)->update('warehouses', [
|
||||
'nama' => $nama
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'update',
|
||||
'Update gudang '.$nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// DELETE
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
// optional: cek relasi ke items
|
||||
$cek = $this->db->get_where('items',['warehouse_id'=>$id])->num_rows();
|
||||
|
||||
if($cek > 0){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->delete('warehouses',['id'=>$id]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'delete',
|
||||
'Delete gudang '.$nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
|
||||
}
|
||||
|
||||
|
||||
public function list()
|
||||
{
|
||||
echo json_encode(
|
||||
$this->db->get('warehouses')->result()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Welcome extends CI_Controller {
|
||||
|
||||
/**
|
||||
* Index Page for this controller.
|
||||
*
|
||||
* Maps to the following URL
|
||||
* http://example.com/index.php/welcome
|
||||
* - or -
|
||||
* http://example.com/index.php/welcome/index
|
||||
* - or -
|
||||
* Since this controller is set as the default controller in
|
||||
* config/routes.php, it's displayed at http://example.com/
|
||||
*
|
||||
* So any other public methods not prefixed with an underscore will
|
||||
* map to /index.php/welcome/<method_name>
|
||||
* @see https://codeigniter.com/userguide3/general/urls.html
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('login');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Directory access is forbidden.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user