Perubahan barcode dan inventory

This commit is contained in:
Wian Drs
2026-07-12 22:10:02 +07:00
parent 3ac6316da4
commit 3113d77242
21 changed files with 2989 additions and 1138 deletions
+5
View File
@@ -2,8 +2,11 @@
application/config/config.php
application/config/database.php
application/controllers/Dbcompare.php
.htaccess
uploads/temp_qr/*.png
application/cache/*
!application/cache/index.html
@@ -32,3 +35,5 @@ user_guide_src/cilexer/pycilexer.egg-info/*
*.sublime-project
/tests/tests/
/tests/results/
+19
View File
@@ -0,0 +1,19 @@
# TODO - Update Cronjob depreciation_asset()
- [x] Review dan lock aturan tanggal penyusutan bulanan berdasarkan tanggal_perolehan
- [x] Update logic due date bulanan per aset (min(day_perolehan, last_day_of_month))
- [x] Pastikan penyusutan mulai bulan berikutnya setelah perolehan
- [x] Batasi eksekusi hanya saat runDate == dueDateThisMonth
- [x] Pertahankan idempotency 1x per bulan
- [x] Samakan tanggal pencatatan mutation dan journal ke dueDateThisMonth
- [x] Verifikasi struktur response details (success/skip reason) tetap informatif
## TODO Baru - Catch-up penyusutan multi-bulan
- [ ] Refactor logic depreciation_asset agar memproses multi-bulan (catch-up)
- [ ] Terapkan loop per bulan dari bulan setelah tanggal patokan sampai bulan berjalan
- [ ] Pertahankan idempotent check per bulan berdasarkan asset_mutations
- [ ] Generate mutasi + jurnal per bulan yang eligible
- [ ] Update nilai buku & akumulasi penyusutan asset setelah proses loop
- [ ] Tambahkan detail response per bulan (processed/skipped reason)
- [ ] Validasi syntax PHP untuk Cronjob.php
+4
View File
@@ -56,3 +56,7 @@ $route['translate_uri_dashes'] = FALSE;
// untuk webhook absensi
$route['iclock/cdata'] = 'AttendanceWebhook/cdata';
// db compare structure
$route['dbcompare/sql'] = 'Dbcompare/sql';
$route['dbcompare/sql_download'] = 'Dbcompare/sql_download';
+133 -20
View File
@@ -6,6 +6,10 @@ class Asset extends CI_Controller {
public function __construct(){
parent::__construct();
$this->load->database();
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index(){
@@ -21,7 +25,7 @@ class Asset extends CI_Controller {
->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')
->order_by('assets.tanggal_perolehan','DESC')
->get()
->result();
@@ -35,6 +39,7 @@ class Asset extends CI_Controller {
$d->kode_asset ?? '-',
$d->nama_asset,
$d->nama_lokasi ?? '-',
$d->keterangan ?? '-',
number_format($d->nilai_perolehan),
$d->masa_manfaat . ' bln',
number_format($d->penyusutan_per_bulan),
@@ -101,11 +106,12 @@ class Asset extends CI_Controller {
'nilai_residu' => $residu,
'penyusutan_per_bulan' => $penyusutan,
'nilai_buku' => $nilai_perolehan,
'sumber' => 'manual',
'sumber' => 'pembelian',
'account_debit_id' => $account_debit_id,
'account_kredit_id' => $account_kredit_id,
'lokasi_asset_id' => $lokasi_asset_id,
'keterangan' => $keterangan
'keterangan' => $keterangan,
'qty' => 1
];
$this->db->insert('assets',$data);
@@ -142,11 +148,18 @@ class Asset extends CI_Controller {
'asset_id' => $asset_id,
'tipe' => 'perolehan',
'nilai' => $nilai_perolehan,
'keterangan' => 'Perolehan manual: '.$keterangan
'keterangan' => 'Perolehan manual: '.$keterangan,
'tanggal_dibuat' => $tanggal
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
if ($this->db->trans_status()) {
log_activity('Asset', 'create', 'Menambah asset manual: '.$nama_asset.' (ID: '.$asset_id.')');
echo json_encode(['status'=>true]);
} else {
echo json_encode(['status'=>false]);
}
}
// ================= SAVE FROM STOCK =================
@@ -157,6 +170,12 @@ class Asset extends CI_Controller {
$masa = (int)$this->input->post('s_masa');
$residu = preg_replace('/\D/','',$this->input->post('s_residu'));
$tanggal = $this->input->post('s_tanggal');
$keterangan = $this->input->post('s_keterangan');
$barcode_id = $this->input->post('barcode_id');
$qty = (int)$this->input->post('qty');
if($qty < 1){
$qty = 1;
}
$item = $this->db->get_where('items',['id'=>$item_id])->row();
@@ -165,15 +184,20 @@ class Asset extends CI_Controller {
return;
}
if($item->stok < 1){
if(!$barcode_id){
echo json_encode(['status'=>false,'message'=>'Barcode wajib dipilih']);
return;
}
if($item->stok < $qty){
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);
$nilai_perolehan = $harga * $qty;
$penyusutan = ($nilai_perolehan - $residu) / ($masa ?: 1);
$this->db->trans_start();
@@ -192,10 +216,43 @@ class Asset extends CI_Controller {
'sumber' => 'gudang',
'account_debit_id' => $account_debit_id,
'account_kredit_id' => 21, // Persediaan
'lokasi_asset_id' => $lokasi_asset_id
'lokasi_asset_id' => $lokasi_asset_id,
'keterangan' => $keterangan,
'qty' => $qty
]);
$asset_id = $this->db->insert_id();
// START - Item Movement & item barcodes update status
$this->db->where('item_id', $item_id);
$this->db->order_by('created_at', 'DESC');
$this->db->limit(1);
$item_move = $this->db->get('item_movements')->row();
// get item_barcode
$itemBar = $this->db->get_where('item_barcodes', ['id'=>$barcode_id])->row();
if($itemBar->qty_sisa < $qty) {
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$itemBar->qty_sisa]);
$this->db->trans_rollback();
return;
}
$total_sisa = $itemBar->qty_sisa - $qty;
$status_bar = ($total_sisa == 0) ? 'installed' : 'available';
$this->db->insert('item_movements',[
'item_id' => $item_id,
'barcode_id' => $barcode_id,
'qty' => $qty,
'from_type' => $item_move ? $item_move->to_type : 'warehouse', //enum('supplier','warehouse','technician','customer','invoice','asset')
'to_type' => 'asset', //enum('supplier','warehouse','technician','customer','invoice','asset')
'movement_type' => 'installation', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment','sold_out','asset')
'notes' => $keterangan,
'created_at' => date('Y-m-d H:i:s')
]);
// Status yang boleh masuk : enum('available','reserved','installed','damaged','lost','returned_supplier','sold_out','asset')
$this->db->where('id', $barcode_id)->update('item_barcodes', ['status' => $status_bar, 'qty_sisa' => $total_sisa]);
// Journal
$this->db->insert('journals',[
@@ -225,9 +282,9 @@ class Asset extends CI_Controller {
'kredit' => $nilai_perolehan
]);
// UPDATE STOK ITEM (kurangi 1)
// UPDATE STOK ITEM (kurangi sesuai qty)
$this->db->where('id', $item_id)
->set('stok', 'stok-1', FALSE)
->set('stok', 'stok-'.$qty, FALSE)
->update('items');
// MUTATION DARI GUDANG
@@ -235,12 +292,18 @@ class Asset extends CI_Controller {
'asset_id' => $asset_id,
'tipe' => 'perolehan',
'nilai' => $nilai_perolehan,
'keterangan' => 'Perolehan dari gudang: '.$nama_asset
'keterangan' => 'Perolehan dari gudang: '.$nama_asset,
'tanggal_dibuat' => $tanggal
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
if ($this->db->trans_status()) {
log_activity('Asset', 'create', 'Menambah asset dari gudang: '.$nama_asset.' (ID: '.$asset_id.', Qty: '.$qty.')');
echo json_encode(['status'=>true]);
} else {
echo json_encode(['status'=>false]);
}
}
// ================= UPDATE =================
@@ -270,7 +333,13 @@ class Asset extends CI_Controller {
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
if ($this->db->trans_status()) {
log_activity('Asset', 'update', 'Update asset ID: '.$id.' Nama: '.$nama_asset);
echo json_encode(['status'=>true]);
} else {
echo json_encode(['status'=>false]);
}
}
// ================= DELETE =================
@@ -302,7 +371,13 @@ class Asset extends CI_Controller {
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
if ($this->db->trans_status()) {
log_activity('Asset', 'delete', 'Hapus asset ID: '.$id.' Alasan: '.$alasan);
echo json_encode(['status'=>true]);
} else {
echo json_encode(['status'=>false]);
}
}
// ================= GET DATA SELECT =================
@@ -326,11 +401,49 @@ class Asset extends CI_Controller {
echo json_encode($data);
}
public function get_items(){
$data = $this->db
->where('stok > 0')
->get('items')
->result();
public function get_items()
{
// $data = $this->db
// ->select('items.*, kode_barang.*')
// ->from('items')
// ->join('kode_barang', 'items.kode_id = kode_barang.id', 'left')
// ->where('items.stok >', 0)
// ->get()
// ->result();
$this->db->select('
items.id,
items.nama_barang,
items.harga_jual,
items.harga_beli,
items.kode_detail,
kode_barang.tracking_type,
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' ,
'left');
$this->db->join('kode_barang',
'items.kode_id = kode_barang.id' ,
'left');
// $this->db->where('items.account_id', $account_id);
$this->db->group_by('items.id');
// ✅ INI KUNCINYA
$this->db->having('stok >', 0);
$data = $this->db->get()->result();
echo json_encode($data);
}
+320 -4
View File
@@ -9,9 +9,9 @@ class Cronjob extends CI_Controller {
$this->load->database();
// ONLY CLI ACCESS
if (!$this->input->is_cli_request()) {
exit('No direct access allowed');
}
// if (!$this->input->is_cli_request()) {
// exit('No direct access allowed');
// }
}
public function generate_insight()
@@ -353,4 +353,320 @@ class Cronjob extends CI_Controller {
}
}
}
// =====================================================
// CRONJOB AUTO PENYUSUTAN ASSET BULANAN
// =====================================================
public function depreciation_asset()
{
$period = date('Y-m');
$runDate = date('Y-m-d');
$akun_beban_penyusutan = 54;
$assets = $this->db
->select('id, nama_asset, nilai_perolehan, nilai_buku, nilai_residu, penyusutan_per_bulan, masa_manfaat, tanggal_perolehan, akumulasi_penyusutan, account_debit_id')
->from('assets')
->where('status', 'aktif')
->where('nilai_buku > nilai_residu', null, false)
->where('penyusutan_per_bulan >', 0)
->get()
->result();
$processed = 0;
$success = 0;
$skipped = 0;
$errors = 0;
$details = [];
$this->db->trans_begin();
try {
foreach ($assets as $a) {
$processed++;
$assetDetails = [
'asset_id' => (int)$a->id,
'asset_name' => $a->nama_asset,
'processed_months' => [],
'skipped_months' => []
];
$account_parent = $this->db
->select('id')
->where('parent_id', $a->account_debit_id)
->get('accounts')
->row();
if (!$account_parent || empty($account_parent->id)) {
$skipped++;
$assetDetails['status'] = 'skip';
$assetDetails['reason'] = 'account_parent_not_found';
$details[] = $assetDetails;
continue;
}
$akun_akumulasi_penyusutan = (int)$account_parent->id;
$mutasiPatokan = $this->db
->select('tanggal_dibuat')
->from('asset_mutations')
->where('asset_id', $a->id)
->where_in('tipe', ['perolehan', 'penambahan'])
->order_by('tanggal_dibuat', 'DESC')
->order_by('id', 'DESC')
->limit(1)
->get()
->row();
$tanggalPatokan = null;
if ($mutasiPatokan && !empty($mutasiPatokan->tanggal_dibuat)) {
$tanggalPatokan = $mutasiPatokan->tanggal_dibuat;
}
if (empty($tanggalPatokan)) {
$tanggalPatokan = $a->tanggal_perolehan;
}
if (empty($tanggalPatokan)) {
$skipped++;
$assetDetails['status'] = 'skip';
$assetDetails['reason'] = 'tanggal_patokan_tidak_ditemukan';
$details[] = $assetDetails;
continue;
}
$startMonth = date('Y-m', strtotime(date('Y-m-01', strtotime($tanggalPatokan)) . ' +1 month'));
$currentMonth = $period;
if ($startMonth > $currentMonth) {
$skipped++;
$assetDetails['status'] = 'skip';
$assetDetails['reason'] = 'start_next_month_after_acquisition';
$assetDetails['tanggal_patokan'] = $tanggalPatokan;
$details[] = $assetDetails;
continue;
}
$nilaiPerolehan = (float)$a->nilai_perolehan;
$nilaiResidu = (float)$a->nilai_residu;
$susutPerBulan = (float)$a->penyusutan_per_bulan;
$masaManfaat = (int)$a->masa_manfaat;
$akumulasiMutasi = $this->db
->select('COALESCE(SUM(nilai),0) total', false)
->from('asset_mutations')
->where('asset_id', $a->id)
->where('tipe', 'penyusutan')
->get()
->row();
$akumulasiBerjalan = (float)($akumulasiMutasi ? $akumulasiMutasi->total : 0);
$nilaiBukuBerjalan = $nilaiPerolehan - $akumulasiBerjalan;
if ($nilaiBukuBerjalan < $nilaiResidu) {
$nilaiBukuBerjalan = $nilaiResidu;
$akumulasiBerjalan = $nilaiPerolehan - $nilaiBukuBerjalan;
}
$iterMonth = $startMonth;
$assetHasSuccess = false;
while ($iterMonth <= $currentMonth) {
$monthsUsed = 0;
$start = new DateTime(date('Y-m-01', strtotime($tanggalPatokan)));
$end = new DateTime(date('Y-m-01', strtotime($iterMonth . '-01')));
$diff = $start->diff($end);
$monthsUsed = ($diff->y * 12) + $diff->m;
if ($monthsUsed < 0) {
$skipped++;
$assetDetails['skipped_months'][] = [
'period' => $iterMonth,
'reason' => 'tanggal_patokan_after_period'
];
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
continue;
}
if ($masaManfaat > 0 && $monthsUsed >= $masaManfaat) {
$skipped++;
$assetDetails['skipped_months'][] = [
'period' => $iterMonth,
'reason' => 'masa_manfaat_finished'
];
break;
}
$acqDay = (int)date('d', strtotime($tanggalPatokan));
$lastDayOfMonth = (int)date('t', strtotime($iterMonth . '-01'));
$dueDay = min($acqDay, $lastDayOfMonth);
$dueDateThisMonth = $iterMonth . '-' . str_pad($dueDay, 2, '0', STR_PAD_LEFT);
// Untuk bulan berjalan, tetap hormati due date.
// Untuk bulan yang sudah lewat, langsung boleh diproses (catch-up).
if ($iterMonth === $currentMonth && strtotime($runDate) < strtotime($dueDateThisMonth)) {
$skipped++;
$assetDetails['skipped_months'][] = [
'period' => $iterMonth,
'reason' => 'not_due_date_today',
'due_date' => $dueDateThisMonth,
'run_date' => $runDate
];
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
continue;
}
$startDate = $iterMonth . '-01';
$endDate = date('Y-m-d', strtotime($startDate . ' +1 month'));
$already = $this->db
->where('asset_id', $a->id)
->where('tipe', 'penyusutan')
->where('tanggal_dibuat >=', $startDate)
->where('tanggal_dibuat <', $endDate)
->count_all_results('asset_mutations');
if ($already > 0) {
$skipped++;
$assetDetails['skipped_months'][] = [
'period' => $iterMonth,
'reason' => 'already_depreciated_this_month'
];
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
continue;
}
if ($nilaiBukuBerjalan <= $nilaiResidu) {
$skipped++;
$assetDetails['skipped_months'][] = [
'period' => $iterMonth,
'reason' => 'nothing_to_depreciate'
];
break;
}
$maxSusut = max(0, $nilaiBukuBerjalan - $nilaiResidu);
$susut = min($susutPerBulan, $maxSusut);
if ($susut <= 0) {
$skipped++;
$assetDetails['skipped_months'][] = [
'period' => $iterMonth,
'reason' => 'nothing_to_depreciate'
];
break;
}
$akumulasiBefore = $akumulasiBerjalan;
$nilaiBukuBefore = $nilaiBukuBerjalan;
$akumulasiBerjalan += $susut;
$nilaiBukuBerjalan = $nilaiPerolehan - $akumulasiBerjalan;
if ($nilaiBukuBerjalan < $nilaiResidu) {
$nilaiBukuBerjalan = $nilaiResidu;
$akumulasiBerjalan = $nilaiPerolehan - $nilaiBukuBerjalan;
}
$this->db->insert('asset_mutations', [
'asset_id' => $a->id,
'tipe' => 'penyusutan',
'nilai' => $susut,
'keterangan' => 'Penyusutan otomatis periode ' . $iterMonth . ' (cronjob)',
'tanggal_dibuat' => $dueDateThisMonth
]);
$no_ref = 'DEP-' . date('YmdHis') . '-' . $a->id . '-' . str_replace('-', '', $iterMonth);
$this->db->insert('journals', [
'tanggal' => $dueDateThisMonth,
'no_ref' => $no_ref,
'keterangan' => 'Penyusutan Asset ' . $a->nama_asset . ' periode ' . $iterMonth,
'ref_type' => 'assets',
'ref_id' => $a->id,
'created_by' => 1
]);
$jid = $this->db->insert_id();
$this->db->insert('journal_details', [
'journal_id' => $jid,
'account_id' => $akun_beban_penyusutan,
'debit' => $susut,
'kredit' => 0
]);
$this->db->insert('journal_details', [
'journal_id' => $jid,
'account_id' => $akun_akumulasi_penyusutan,
'debit' => 0,
'kredit' => $susut
]);
$success++;
$assetHasSuccess = true;
$assetDetails['processed_months'][] = [
'period' => $iterMonth,
'depreciation' => (float)$susut,
'due_date' => $dueDateThisMonth,
'akumulasi_before' => (float)$akumulasiBefore,
'akumulasi_after' => (float)$akumulasiBerjalan,
'nilai_buku_before' => (float)$nilaiBukuBefore,
'nilai_buku_after' => (float)$nilaiBukuBerjalan,
'journal_no_ref' => $no_ref
];
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
}
if ($assetHasSuccess) {
$this->db->where('id', $a->id)->update('assets', [
'akumulasi_penyusutan' => $akumulasiBerjalan,
'nilai_buku' => $nilaiBukuBerjalan
]);
$assetDetails['status'] = 'success';
$assetDetails['akumulasi_final'] = (float)$akumulasiBerjalan;
$assetDetails['nilai_buku_final'] = (float)$nilaiBukuBerjalan;
} else {
$assetDetails['status'] = 'skip';
if (empty($assetDetails['reason'])) {
$assetDetails['reason'] = 'no_eligible_month_to_process';
}
}
$details[] = $assetDetails;
}
if ($this->db->trans_status() === FALSE) {
throw new Exception('Transaction failed');
}
$this->db->trans_commit();
echo json_encode([
'status' => true,
'period' => $period,
'processed' => $processed,
'success' => $success,
'skipped' => $skipped,
'errors' => $errors,
'message' => 'Depreciation cron executed',
'details' => $details
]);
} catch (Exception $e) {
$this->db->trans_rollback();
$errors++;
echo json_encode([
'status' => false,
'period' => $period,
'processed' => $processed,
'success' => $success,
'skipped' => $skipped,
'errors' => $errors,
'message' => $e->getMessage()
]);
}
}
}
+2
View File
@@ -135,6 +135,8 @@ class Customers extends CI_Controller {
// =========================
public function delete($id)
{
$customer = $this->dm->setTable('customers')->get_by_id($id);
$this->dm->setTable('customers')->delete($id);
// ================= LOG ACTIVITY =================
+26
View File
@@ -10,6 +10,10 @@ class Employees extends CI_Controller {
$this->load->database();
$this->load->helper(['url', 'file']);
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
date_default_timezone_set('Asia/Jakarta');
}
@@ -192,6 +196,14 @@ class Employees extends CI_Controller {
$data['photo'] = $photo;
$this->db->insert('k_employees',$data);
$employee_id = $this->db->insert_id();
log_activity(
'employees',
'create',
'Menambah karyawan: '.$data['full_name'].' (ID: '.$employee_id.')',
'success'
);
return $this->json([
'status' => true,
@@ -247,6 +259,13 @@ class Employees extends CI_Controller {
->where('id',$id)
->update('k_employees',$data);
log_activity(
'employees',
'update',
'Update karyawan ID: '.$id.' Nama: '.($data['full_name'] ?? '-'),
'success'
);
return $this->json([
'status' => true,
'message' => 'Berhasil update data'
@@ -292,6 +311,13 @@ class Employees extends CI_Controller {
'id'=>$id
]);
log_activity(
'employees',
'delete',
'Hapus karyawan ID: '.$id.' Nama: '.($employee->full_name ?? '-'),
'success'
);
return $this->json([
'status' => true,
'message' => 'Berhasil hapus data'
+94 -115
View File
@@ -27,114 +27,81 @@ class Generateneracasaldo extends CI_Controller {
}
// =========================
// 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;
}
// =========================
// AMBIL DATA NERACA SALDO (DIPAKAI BERSAMA OLEH get_data() & generatepdf())
// AMBIL DATA NERACA SALDO
// DIPAKAI OLEH AJAX & PDF
// =========================
private function get_neracasaldo_data()
{
$this->db->select('
$tanggal_awal = $this->input->get('tanggal_awal');
$tanggal_akhir = $this->input->get('tanggal_akhir');
$this->db->select("
accounts.id,
accounts.kode_akun,
accounts.nama_akun,
accounts.kategori,
accounts.tipe,
accounts.posisi,
COALESCE(SUM(journal_details.debit),0) as debit,
COALESCE(SUM(journal_details.kredit),0) as kredit
');
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');
// ======================================
// FILTER PERIODE
// ======================================
if (!empty($tanggal_awal) && !empty($tanggal_akhir)) {
$this->db->group_by('accounts.id');
$this->db->order_by('accounts.kode_akun','ASC');
$this->db->join(
"(SELECT jd.*
FROM journal_details jd
JOIN journals j ON j.id = jd.journal_id
WHERE j.tanggal BETWEEN ".$this->db->escape($tanggal_awal)."
AND ".$this->db->escape($tanggal_akhir)."
) journal_details",
"journal_details.account_id = accounts.id",
"left",
false
);
$rows = $this->db->get()->result();
} else {
// =========================
// HITUNG LABA BERJALAN
// =========================
$laba = $this->get_laba_berjalan();
$this->db->join(
'journal_details',
'journal_details.account_id = accounts.id',
'left'
);
}
$rows = $this->db
->where('accounts.is_active',1)
->group_by('accounts.id')
->order_by('accounts.kode_akun','ASC')
->get()
->result();
$result = [];
$total_debit = 0;
$total_kredit = 0;
foreach ($rows as $r) {
foreach($rows as $row){
// ================= SALDO NORMAL
if ($r->posisi == 'debit') {
$saldo = $r->debit - $r->kredit;
} else {
$saldo = $r->kredit - $r->debit;
}
$row->debit = (float)$row->debit;
$row->kredit = (float)$row->kredit;
// ================= 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);
}
$total_debit += $r->saldo_debit;
$total_kredit += $r->saldo_kredit;
$result[] = [
'kode' => $r->kode_akun,
'nama' => $r->nama_akun,
'saldo_debit' => $r->saldo_debit,
'saldo_kredit' => $r->saldo_kredit,
];
$total_debit += $row->debit;
$total_kredit += $row->kredit;
}
return [
'rows' => $rows,
'data' => $result,
'total_debit' => $total_debit,
'total_kredit' => $total_kredit,
'tanggal_awal' => $tanggal_awal,
'tanggal_akhir' => $tanggal_akhir,
'rows' => $rows,
'total_debit' => $total_debit,
'total_kredit' => $total_kredit
];
}
// =========================
// GET DATA NERACA SALDO (JSON, untuk AJAX/datatable)
// =========================
public function get_data()
{
$result = $this->get_neracasaldo_data();
@@ -142,68 +109,80 @@ class Generateneracasaldo extends CI_Controller {
echo json_encode($result['rows']);
}
// =========================
// PDF GENERATE — NERACA SALDO
// =========================
public function generatepdf()
{
if ($this->session->userdata('role') != 'Admin') {
show_error('Akses ditolak', 403);
show_error('Akses ditolak',403);
}
if (ob_get_length()) {
ob_end_clean();
}
// =============================================================
// AMBIL DATA
// =============================================================
// ==========================================
// DATA
// ==========================================
$neracasaldo = $this->get_neracasaldo_data();
// =============================================================
// DATA PERUSAHAAN
// =============================================================
// ==========================================
// PERUSAHAAN
// ==========================================
$company = $this->db->get('pengaturan')->row();
// =============================================================
// BUILD STRING PERIODE
// =============================================================
$periodeStr = 'Per Tanggal : ' . date('d-m-Y');
// ==========================================
// PERIODE
// ==========================================
if(!empty($neracasaldo['tanggal_awal']) && !empty($neracasaldo['tanggal_akhir'])){
// =============================================================
// LOAD LIBRARY PDF
// =============================================================
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
require_once(APPPATH . 'libraries/PDF_Neracasaldo.php');
$periodeStr = 'Periode : '
. date('d-m-Y',strtotime($neracasaldo['tanggal_awal']))
.' s/d '
. date('d-m-Y',strtotime($neracasaldo['tanggal_akhir']));
$pdf = new PDF ('P', 'mm', 'A4');
$pdf->SetMargins(10, 10, 10);
}else{
$periodeStr = 'Seluruh Periode';
}
// ==========================================
// PDF
// ==========================================
require_once(APPPATH.'third_party/fpdf/fpdf.php');
require_once(APPPATH.'libraries/PDF_Neracasaldo.php');
$pdf = new PDF('P','mm','A4');
$pdf->SetMargins(10,10,10);
$pdf->setHeaderData(
$company->nama_perusahaan,
$company->alamat_perusahaan,
FCPATH . 'uploads/img/logo-ljn.png'
FCPATH.'uploads/img/logo-ljn.png'
);
$pdf->setInfoLaporan($periodeStr);
$pdf->AddPage('P', 'A4');
$pdf->AddPage();
$pdf->NeracaSaldoTable(
$neracasaldo['data'],
$neracasaldo['rows'],
$neracasaldo['total_debit'],
$neracasaldo['total_kredit']
);
$pdf->NeracaSaldoRingkasan(
$neracasaldo['total_debit'],
$neracasaldo['total_kredit']
);
$pdf->Pembuat('Tito Ngudiana', 'Finance Department');
// =============================================================
// OUTPUT
// =============================================================
$filename = 'Neraca_Saldo_' . date('YmdHis') . '.pdf';
$pdf->Output('I', $filename);
$pdf->Pembuat(
$this->session->userdata('nama') ?: 'Administrator',
'Finance Department'
);
$filename = 'Neraca_Saldo_'.date('YmdHis').'.pdf';
$pdf->Output('I',$filename);
exit;
}
}
+168 -91
View File
@@ -367,7 +367,9 @@ class Invoices extends CI_Controller {
'warehouse_id' => null,
'keterangan' => $inv_items->keterangan,
'subtotal_asli' => $inv_items->subtotal_asli,
'is_cicilan' => $inv_items->is_cicilan
'is_cicilan' => $inv_items->is_cicilan,
'source_cicilan_id' => $c->id,
'total_hpp_barang' => $c->cicilan_hpp_barang
];
$this->db->insert('invoice_details', $insert_data);
@@ -476,13 +478,14 @@ class Invoices extends CI_Controller {
public function detail($id)
{
// GET customer_id
$row = $this->db->select('customer_id')
$row = $this->db->select('customer_id, no_invoice')
->where('id',$id)
->get('invoices')
->row();
$data['active_menu'] = "invoice_draft";
$data['invoice_id'] = $id;
$data['no_invoice'] = $row ? $row->no_invoice : null;
$data['customer_id'] = $row ? $row->customer_id : null;
$this->load->view('partials/header', $data);
@@ -554,6 +557,7 @@ class Invoices extends CI_Controller {
$harga = (float)$this->input->post('harga');
$account_id = $this->input->post('account_id');
$warehouse_id = $this->input->post('warehouse_id');
$barcode_id = $this->input->post('barcode_id');
$total_harga = $qty * $harga;
$total_hpp = 0;
@@ -561,7 +565,9 @@ class Invoices extends CI_Controller {
// jika di bayar cicilan
$is_cicilan = $this->input->post('is_cicilan') ?? null;
$tenor = (int)$this->input->post('tenor') ?? 0;
// ambil invoice Detail
$invoice = $this->db->get_where('invoices', ['id' => $invoice_id])->row();
// =========================
// VALIDASI & AMBIL ITEM
// =========================
@@ -576,12 +582,44 @@ class Invoices extends CI_Controller {
$nama_item = $item->nama_barang;
// cek stok
$stok = $this->get_stok($item_id);
if($stok < $qty){
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$stok]);
// $stok = $this->get_stok($item_id);
// if($stok < $qty){
// echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$stok]);
// $this->db->trans_rollback();
// return;
// }
// START - Item Movement & item barcodes update status
$this->db->where('item_id', $item_id);
$this->db->order_by('created_at', 'DESC');
$this->db->limit(1);
$item_move = $this->db->get('item_movements')->row();
// get item_barcode
$itemBar = $this->db->get_where('item_barcodes', ['id'=>$barcode_id])->row();
if($itemBar->qty_sisa < $qty) {
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$itemBar->qty_sisa]);
$this->db->trans_rollback();
return;
}
$total_sisa = $itemBar->qty_sisa - $qty;
$status_bar = ($total_sisa == 0) ? 'sold_out' : 'available';
$this->db->insert('item_movements',[
'item_id' => $item_id,
'barcode_id' => $barcode_id,
'qty' => $qty,
'from_type' => $item_move->to_type, //enum('supplier','warehouse','technician','customer','invoice')
'to_type' => 'invoice', //enum('supplier','warehouse','technician','customer','invoice')
'movement_type' => 'sold_out', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment','sold_out')
'notes' => 'Barang terjual ke INV : ' . $invoice->no_invoice,
'created_at' => date('Y-m-d H:i:s')
]);
$this->db->where('id', $barcode_id)->update('item_barcodes', ['status' => $status_bar, 'qty_sisa' => $total_sisa]);
// END - Item Movement & item barcodes update status
$total_hpp = $qty * $item->harga_beli;
}
@@ -600,6 +638,9 @@ class Invoices extends CI_Controller {
// harga per cicilan
$harga_cicilan = round($total_harga / $tenor);
//ngatur HPP Cicilan
$total_hpp = round($total_hpp / $tenor);
$harga_simpan = $harga_cicilan;
$subtotal_simpan = $harga_cicilan;
@@ -624,7 +665,8 @@ class Invoices extends CI_Controller {
'keterangan' => $this->input->post('keterangan'),
'subtotal_asli' => $harga_asli,
'is_cicilan' => $is_cicilan ? 1 : 0,
'total_hpp_barang' => $total_hpp
'total_hpp_barang' => $total_hpp,
'barcode_id' => $barcode_id
];
$this->db->insert('invoice_details', $insert_data);
@@ -639,8 +681,6 @@ class Invoices extends CI_Controller {
$this->update_total($invoice_id);
// ambil customer dari invoice
$invoice = $this->db->get_where('invoices', ['id' => $invoice_id])->row();
if ($is_cicilan && $tenor > 0) {
@@ -653,6 +693,7 @@ class Invoices extends CI_Controller {
$tenor,
$tanggal,
$account_id,
$total_hpp,
$item_id ?: null
);
}
@@ -675,105 +716,108 @@ class Invoices extends CI_Controller {
}
}
// =========================
// UPDATE ITEM - RESPONSE KONSISTEN
// =========================
public function update_item()
{
$this->db->trans_begin();
// // =========================
// // UPDATE ITEM - RESPONSE KONSISTEN
// // =========================
// public function update_item()
// {
// $this->db->trans_begin();
$id = $this->input->post('id');
$old = $this->db->get_where('invoice_details', ['id'=>$id])->row();
// $id = $this->input->post('id');
// $old = $this->db->get_where('invoice_details', ['id'=>$id])->row();
if(!$old){
echo json_encode(['status'=>false, 'message'=>'Item tidak ditemukan']);
return;
}
// if(!$old){
// echo json_encode(['status'=>false, 'message'=>'Item tidak ditemukan']);
// return;
// }
$cek = $this->db->get_where('invoice_installments', [
'invoice_detail_id' => $id
])->num_rows();
// $cek = $this->db->get_where('invoice_installments', [
// 'invoice_detail_id' => $id
// ])->num_rows();
if ($cek > 0) {
echo json_encode([
'status' => false,
'message' => 'Item cicilan tidak bisa diubah. Hapus dan buat ulang.'
]);
return;
}
// if ($cek > 0) {
// echo json_encode([
// 'status' => false,
// 'message' => 'Item cicilan tidak bisa diubah. Hapus dan buat ulang.'
// ]);
// return;
// }
// 🔥 REVERSE DATA LAMA
$this->delete_jurnal_item($id);
$this->delete_stok_item($id);
// // 🔥 REVERSE DATA LAMA
// $this->delete_jurnal_item($id);
// $this->delete_stok_item($id);
// DATA BARU
$item_id = $this->input->post('item_id');
$nama_item = $this->input->post('nama_item');
$tanggal = $this->input->post('tanggal');
// // DATA BARU
// $item_id = $this->input->post('item_id');
// $nama_item = $this->input->post('nama_item');
// $tanggal = $this->input->post('tanggal');
$total_hpp = 0;
// $total_hpp = 0;
if($item_id){
$item = $this->db->get_where('items', ['id'=>$item_id])->row();
if($item){
$nama_item = $item->nama_barang;
$stok = $this->get_stok($item_id);
$qty = (float)$this->input->post('qty');
$total_hpp = $this->input->post('qty') * $item->harga_beli;
if($stok < $qty){
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup']);
return;
}
}
}
// if($item_id){
// $item = $this->db->get_where('items', ['id'=>$item_id])->row();
// if($item){
// $nama_item = $item->nama_barang;
// $stok = $this->get_stok($item_id);
// $qty = (float)$this->input->post('qty');
// $total_hpp = $this->input->post('qty') * $item->harga_beli;
// if($stok < $qty){
// echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup']);
// return;
// }
// }
// }
$qty = (float)$this->input->post('qty');
$harga = (float)$this->input->post('harga');
// $qty = (float)$this->input->post('qty');
// $harga = (float)$this->input->post('harga');
// UPDATE
$update_data = [
'tanggal' => $tanggal,
'account_id' => $this->input->post('account_id'),
'items_id' => $item_id ?: null,
'nama_item' => $nama_item,
'qty' => $qty,
'harga' => $harga,
'subtotal' => $qty * $harga,
'warehouse_id' => $this->input->post('warehouse_id'),
'keterangan' => $this->input->post('keterangan'),
'total_hpp_barang' => $total_hpp
];
// // UPDATE
// $update_data = [
// 'tanggal' => $tanggal,
// 'account_id' => $this->input->post('account_id'),
// 'items_id' => $item_id ?: null,
// 'nama_item' => $nama_item,
// 'qty' => $qty,
// 'harga' => $harga,
// 'subtotal' => $qty * $harga,
// 'warehouse_id' => $this->input->post('warehouse_id'),
// 'keterangan' => $this->input->post('keterangan'),
// 'total_hpp_barang' => $total_hpp
// ];
$this->db->where('id', $id)->update('invoice_details', $update_data);
// $this->db->where('id', $id)->update('invoice_details', $update_data);
// GENERATE ULANG
if($this->input->post('account_id') != 70) {
$this->generate_jurnal_item($id);
}
$this->update_total($old->invoice_id);
// // GENERATE ULANG
// if($this->input->post('account_id') != 70) {
// $this->generate_jurnal_item($id);
// }
// $this->update_total($old->invoice_id);
// ✅ RESPONSE KONSISTEN DENGAN JS
if ($this->db->trans_status() === FALSE){
$this->db->trans_rollback();
echo json_encode(['status'=>false, 'message'=>'Update gagal']);
} else {
$this->db->trans_commit();
// // ✅ RESPONSE KONSISTEN DENGAN JS
// if ($this->db->trans_status() === FALSE){
// $this->db->trans_rollback();
// echo json_encode(['status'=>false, 'message'=>'Update gagal']);
// } else {
// $this->db->trans_commit();
log_activity(
'invoice',
'update_item',
'Update item invoice detail ID ' . $id,
'success'
);
// log_activity(
// 'invoice',
// 'update_item',
// 'Update item invoice detail ID ' . $id,
// 'success'
// );
echo json_encode(['status'=>true, 'message'=>'Item berhasil diupdate']);
}
}
// echo json_encode(['status'=>true, 'message'=>'Item berhasil diupdate']);
// }
// }
// =========================
// DELETE ITEM - RESPONSE KONSISTEN
// =========================
public function delete_item($id)
public function delete_item($id, $no_invoice)
{
$this->db->trans_begin();
@@ -792,6 +836,36 @@ class Invoices extends CI_Controller {
$this->delete_stok_item($id);
$this->db->delete('invoice_details', ['id'=>$id]);
$this->update_total($d->invoice_id);
if ($d->items_id AND $d->is_cicilan != 1) {
// START - Item Movement & item barcodes update status
$this->db->where('item_id', $d->items_id);
$this->db->order_by('created_at', 'DESC');
$this->db->limit(1);
$item_move = $this->db->get('item_movements')->row();
// get item_barcode
$itemBar = $this->db->get_where('item_barcodes', ['id'=>$d->barcode_id])->row();
$total_sisa = $itemBar->qty_sisa + $d->qty;
$status_bar = ($total_sisa == 0) ? 'sold_out' : 'available';
$this->db->insert('item_movements',[
'item_id' => $d->items_id,
'barcode_id' => $d->barcode_id,
'qty' => $d->qty,
'from_type' => $item_move->to_type, //enum('supplier','warehouse','technician','customer','invoice')
'to_type' => 'warehouse', //enum('supplier','warehouse','technician','customer','invoice')
'movement_type' => 'purchase', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment','sold_out')
'notes' => 'Barang di kembalikan ke gudang dari : ' . $no_invoice,
'created_at' => date('Y-m-d H:i:s')
]);
// $this->db->where('id', $d->barcode_id)->update('item_barcodes', ['status' => 'available']);
$this->db->where('id', $d->barcode_id)->update('item_barcodes', ['status' => $status_bar, 'qty_sisa' => $total_sisa]);
// END - Item Movement & item barcodes update status
}
// ✅ RESPONSE KONSISTEN DENGAN JS
if ($this->db->trans_status() === FALSE){
@@ -1223,7 +1297,7 @@ class Invoices extends CI_Controller {
}
private function generate_installments($invoice_id, $detail_id, $customer_id, $total, $tenor, $start_date, $account_id, $item_id = null)
private function generate_installments($invoice_id, $detail_id, $customer_id, $total, $tenor, $start_date, $account_id, $total_hpp, $item_id = null)
{
$nominal = round($total / $tenor);
$sisa = $total - ($nominal * $tenor);
@@ -1250,6 +1324,7 @@ class Invoices extends CI_Controller {
'nominal' => $nilai,
'account_id' => $account_id,
'item_id' => $item_id ?: null,
'cicilan_hpp_barang' => $total_hpp,
'status' => 'unpaid',
'is_billed' => $is_billed
]);
@@ -1336,7 +1411,8 @@ class Invoices extends CI_Controller {
'keterangan' => $inv_items->keterangan,
'subtotal_asli' => $inv_items->subtotal_asli,
'is_cicilan' => $inv_items->is_cicilan,
'source_cicilan_id' => $cicilan->id
'source_cicilan_id' => $cicilan->id,
'total_hpp_barang' => $cicilan->cicilan_hpp_barang
]);
// =========================
@@ -1498,6 +1574,7 @@ public function bayar()
invoice_details.recognized_amount,
invoice_details.total_hpp_barang,
invoice_details.recognized_hpp,
invoice_details.is_cicilan,
accounts.priority
')
->from('invoice_details')
+461 -159
View File
@@ -40,6 +40,98 @@ class Items extends CI_Controller {
$this->load->view('partials/footer');
}
public function item_barcodes_history()
{
$data = [
"active_menu" => "item_historys"
];
$this->load->view('partials/header', $data);
$this->load->view('items/item_historys', $data);
$this->load->view('partials/footer');
}
public function scan_history()
{
$barcode = trim($this->input->post('barcode'));
if(empty($barcode)){
echo json_encode([
'status'=>false,
'message'=>'QR ID tidak boleh kosong'
]);
return;
}
$item = $this->db
->select("
item_barcodes.id,
item_barcodes.barcode,
item_barcodes.serial_number,
item_barcodes.status,
item_barcodes.qty_awal,
item_barcodes.qty_sisa,
items.nama_barang,
items.kode_detail
")
->from('item_barcodes')
->join('items','items.id=item_barcodes.item_id')
->where('item_barcodes.barcode',$barcode)
->or_where('item_barcodes.serial_number',$barcode)
->get()
->row();
if(!$item){
echo json_encode([
'status'=>false,
'message'=>'Barcode tidak ditemukan.'
]);
return;
}
$history = $this->db
->select("
item_movements.*,
w1.nama as from_warehouse,
w2.nama as to_warehouse
")
->from('item_movements')
->join(
'warehouses w1',
'w1.id=item_movements.from_id AND item_movements.from_type="warehouse"',
'left'
)
->join(
'warehouses w2',
'w2.id=item_movements.to_id AND item_movements.to_type="warehouse"',
'left'
)
->where('barcode_id',$item->id)
->order_by('created_at','DESC')
->get()
->result();
echo json_encode([
'status'=>true,
'item'=>$item,
'history'=>$history
]);
}
public function load_data_barcode($item_id)
{
$item = $this->db
@@ -294,92 +386,103 @@ class Items extends CI_Controller {
$this->load_data_barcode($barcode->item_id);
}
public function get_barcode_item()
{
$barcode = trim($this->input->post('barcode'));
public function get_barcode_item()
{
$barcode = trim($this->input->post('barcode'));
if ($barcode == '') {
echo json_encode([
'status' => false,
'message' => 'Barcode kosong.'
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i', 'i.id = ib.item_id')
->where('ib.qty_sisa !=', 0)
->where('i.status', 'active')
->group_start()
->where('ib.barcode', $barcode)
->or_like('i.nama_barang', $barcode, 'both')
->or_like('ib.serial_number', $barcode, 'both')
->group_end()
->limit(15)
->get()
->result();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Barcode tidak ditemukan.',
'barcode' => $barcode
]);
return;
}
if ($barcode == '') {
echo json_encode([
'status' => false,
'message' => 'Barcode kosong.'
'status'=>true,
'data'=>$row
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i','i.id=ib.item_id')
->where('ib.barcode',$barcode)
->get()
->row();
public function get_list_barcode_id($item_id)
{
// $item_id = trim($this->input->post('item_id'));
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Barcode tidak ditemukan.'
]);
return;
if ($item_id == '') {
echo json_encode([
'status' => false,
'message' => 'Item belum dipilih'
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
ib.qty_awal,
ib.qty_sisa,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i','i.id=ib.item_id')
->where('ib.item_id',$item_id)
->where('ib.qty_sisa !=', 0)
->where_in('ib.status', ['installed', 'reserved', 'available'])
->get()
->result();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Item barang tidak ditemukan.'
]);
return;
}
echo json_encode($row);
}
echo json_encode([
'status'=>true,
'data'=>$row
]);
}
public function get_list_barcode_id($item_id)
{
// $item_id = trim($this->input->post('item_id'));
if ($item_id == '') {
echo json_encode([
'status' => false,
'message' => 'Item belum dipilih'
]);
return;
}
$row = $this->db
->select('
ib.id AS barcode_id,
ib.barcode,
ib.serial_number,
i.id AS item_id,
i.kode_detail,
i.nama_barang,
i.status,
i.harga_beli,
i.harga_jual
')
->from('item_barcodes ib')
->join('items i','i.id=ib.item_id')
->where('ib.item_id',$item_id)
// ->where('ib.status','warehouse')
->get()
->result();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Item barang tidak ditemukan.'
]);
return;
}
echo json_encode($row);
}
// =========================
// GET DATA (FIX)
@@ -484,7 +587,7 @@ public function get_list_barcode_id($item_id)
} else {
$btnAdjust = '';
}
$action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete . $btnSN;
$action = $btnDetail . $btnEdit . $btnAdjust . $btnSN;
}
@@ -578,7 +681,8 @@ public function get_list_barcode_id($item_id)
if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || !$account_kas){
echo json_encode([
'status'=>false,
'message'=>'Data tidak lengkap / tidak valid'
'message'=>'Data tidak lengkap / tidak valid',
'account_id' => $account_kas
]);
return;
}
@@ -655,7 +759,9 @@ public function get_list_barcode_id($item_id)
// ================= Item Barcode Masuk =================
for ($i = 1; $i <= $unit_qty; $i++) {
$barcode = sprintf('%s-%04d', $kode_detail, $i);
// $barcode = sprintf('%s-%04d', $kode_detail, $i);
$barcode = strtoupper($kode_detail) . '-' . strtoupper(bin2hex(random_bytes(2)));
$this->db->insert('item_barcodes',[
'item_id' => $item_id,
@@ -680,39 +786,6 @@ public function get_list_barcode_id($item_id)
'created_at' => date('Y-m-d H:i:s')
]);
}
// CREATE TABLE `item_barcodes` (
// `id` int NOT NULL AUTO_INCREMENT,
// `item_id` int NOT NULL,
// `barcode` varchar(100) NOT NULL,
// `serial_number` varchar(100) DEFAULT NULL,
// `warehouse_id` int DEFAULT NULL,
// `qty_awal` decimal(15,2) DEFAULT NULL,
// `qty_sisa` decimal(15,2) DEFAULT NULL,
// `status` enum('available','reserved','installed','damaged','lost','returned_supplier') DEFAULT 'available',
// `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
// PRIMARY KEY (`id`),
// UNIQUE KEY `uk_barcode` (`barcode`),
// KEY `idx_item` (`item_id`),
// KEY `idx_warehouse` (`warehouse_id`),
// CONSTRAINT `fk_barcode_item` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
// CREATE TABLE `item_movements` (
// `id` int NOT NULL AUTO_INCREMENT,
// `item_id` int NOT NULL,
// `barcode_id` int DEFAULT NULL,
// `qty` decimal(15,2) NOT NULL DEFAULT '1.00',
// `from_type` enum('supplier','warehouse','technician','customer') DEFAULT NULL,
// `from_id` int DEFAULT NULL,
// `to_type` enum('supplier','warehouse','technician','customer') DEFAULT NULL,
// `to_id` int DEFAULT NULL,
// `movement_type` enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment') NOT NULL,
// `reference_type` varchar(50) DEFAULT NULL,
// `reference_id` int DEFAULT NULL,
// `notes` varchar(255) DEFAULT NULL,
// `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
// ================= JURNAL =================
$no_ref = 'BLI-'.date('Ymd').'-'.$item_id;
@@ -737,12 +810,16 @@ public function get_list_barcode_id($item_id)
]);
// 🔥 Kredit Kas (DINAMIS)
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_kas,
'debit'=>0,
'kredit'=>$total
]);
foreach ($account_kas as $row) {
$this->db->insert('journal_details', [
'journal_id' => $jid,
'account_id' => $row['account_id'],
'debit' => 0,
'kredit' => str_replace('.', '', $row['nominal'])
]);
}
$this->db->trans_complete();
@@ -988,33 +1065,157 @@ public function get_list_barcode_id($item_id)
// =========================
public function keluarkan()
{
$account_biaya = (int)$this->input->post('account_biaya');
$tanggal_keluar = $this->input->post('tanggal_keluar');
$account_biaya = (int)$this->input->post('account_biaya');
$tanggal_keluar = $this->input->post('tanggal_keluar');
$keterangan_keluar = $this->input->post('keterangan_keluar');
// mode baru: single barcode terpilih
$selected_barcode = (int)$this->input->post('selected_barcode');
if ($selected_barcode > 0) {
if (!$account_biaya || !$tanggal_keluar) {
echo json_encode(['status'=>false,'message'=>'Data tidak lengkap']);
return;
}
$barcode = $this->db
->select('
ib.id as barcode_id,
ib.item_id,
ib.qty_sisa,
ib.warehouse_id,
ib.status as barcode_status,
i.nama_barang,
i.harga_beli
')
->from('item_barcodes ib')
->join('items i', 'i.id = ib.item_id')
->where('ib.id', $selected_barcode)
->get()
->row();
if (!$barcode) {
echo json_encode(['status'=>false,'message'=>'Barcode tidak ditemukan']);
return;
}
if ((float)$barcode->qty_sisa <= 0) {
echo json_encode(['status'=>false,'message'=>'Qty barcode sudah habis']);
return;
}
if (empty($barcode->warehouse_id)) {
echo json_encode(['status'=>false,'message'=>'Warehouse barcode tidak valid']);
return;
}
$qty_keluar = 1;
$nilai = $qty_keluar * (float)$barcode->harga_beli;
$this->db->trans_start();
// stock log keluar per item
$this->db->insert('stock_logs',[
'item_id' => $barcode->item_id,
'warehouse_id' => $barcode->warehouse_id,
'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').'-'.$barcode->item_id,
'keterangan' => 'Pengeluaran Barang '.$barcode->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();
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_biaya,
'debit'=>$nilai,
'kredit'=>0
]);
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>0,
'kredit'=>$nilai
]);
// START - Item Movement & item barcodes update status
$this->db->where('barcode_id', $barcode->barcode_id);
$this->db->order_by('created_at', 'DESC');
$this->db->limit(1);
$item_move = $this->db->get('item_movements')->row();
if($barcode->qty_sisa < $qty_keluar) {
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$barcode->qty_sisa]);
$this->db->trans_rollback();
return;
}
$this->db->insert('item_movements',[
'item_id' => $barcode->item_id,
'barcode_id' => $barcode->barcode_id,
'qty' => $qty_keluar,
'from_type' => $item_move ? $item_move->to_type : 'warehouse', //enum('supplier','warehouse','technician','customer','invoice','asset')
'to_type' => 'technician', //enum('supplier','warehouse','technician','customer','invoice','asset')
'movement_type' => 'installation', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment','sold_out','asset')
'notes' => $keterangan_keluar,
'created_at' => date('Y-m-d H:i:s')
]);
// update qty_sisa barcode
$new_qty_sisa = (float)$barcode->qty_sisa - $qty_keluar;
$this->db->where('id', $barcode->barcode_id)->update('item_barcodes', [
'qty_sisa' => $new_qty_sisa,
'status' => $new_qty_sisa <= 0 ? 'installed' : $barcode->barcode_status
]);
// END - Item Movement & item barcodes update status
$this->db->trans_complete();
if (!$this->db->trans_status()) {
echo json_encode(['status'=>false,'message'=>'Gagal proses']);
return;
}
$this->update_item_stok($barcode->item_id);
log_activity(
'items',
'stock_out',
'Keluar barcode: '.$barcode->barcode_id.' | '.$barcode->nama_barang.' qty: '.$qty_keluar.' ( '.$keterangan_keluar.' )',
'success'
);
echo json_encode(['status'=>true,'message'=>'Barang berhasil dikeluarkan']);
return;
}
// fallback flow lama
$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
')
@@ -1022,20 +1223,16 @@ public function get_list_barcode_id($item_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,
@@ -1043,10 +1240,9 @@ public function get_list_barcode_id($item_id)
'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,
@@ -1055,44 +1251,39 @@ public function get_list_barcode_id($item_id)
'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']);
}
@@ -1232,4 +1423,115 @@ public function get_list_barcode_id($item_id)
echo json_encode($data);
}
public function update_barcode_item()
{
$items = $this->db
->select("
i.id,
i.nama_barang,
i.stok,
i.kode_detail,
kb.tracking_type
")
->from('items i')
->join('kode_barang kb', 'kb.id=i.kode_id')
->where('i.status', 'active')
->get()
->result();
$this->db->trans_begin();
foreach ($items as $item) {
// ===========================================
// Hitung jumlah barcode yang seharusnya
// ===========================================
if ($item->tracking_type == 'UNIT') {
$target = (int)$item->stok;
} else {
$target = (int)floor($item->stok / 1000);
}
if ($target <= 0) {
continue;
}
// ===========================================
// Barcode yang sudah ada
// ===========================================
$barcode_exist = $this->db
->where('item_id', $item->id)
->count_all_results('item_barcodes');
if ($barcode_exist >= $target) {
continue;
}
$need = $target - $barcode_exist;
for ($i = 1; $i <= $need; $i++) {
$barcode = strtoupper($item->kode_detail) . '-' . strtoupper(bin2hex(random_bytes(2)));
$qty = ($item->tracking_type == 'UNIT')
? 1
: 1000;
// =============================
// Insert Barcode
// =============================
$this->db->insert('item_barcodes', [
'item_id' => $item->id,
'barcode' => $barcode,
'warehouse_id' => null,
'qty_awal' => $qty,
'qty_sisa' => $qty,
'status' => 'available'
]);
$barcode_id = $this->db->insert_id();
// =============================
// Insert Movement
// =============================
$this->db->insert('item_movements', [
'item_id' => $item->id,
'barcode_id' => $barcode_id,
'qty' => $qty,
'from_type' => 'supplier',
'from_id' => null,
'to_type' => 'warehouse',
'to_id' => null,
'movement_type' => 'purchase',
'reference_type' => 'system_generate',
'reference_id' => null,
'notes' => 'Generate barcode otomatis'
]);
}
}
if ($this->db->trans_status() == false) {
$this->db->trans_rollback();
echo json_encode([
'status' => false,
'message' => 'Gagal generate barcode.'
]);
return;
}
$this->db->trans_commit();
echo json_encode([
'status' => true,
'message' => 'Barcode berhasil diperbarui.'
]);
}
}
+97 -24
View File
@@ -25,7 +25,7 @@ class Kodebarang extends CI_Controller {
// =========================
public function get_data()
{
$data = $this->db->get('kode_barang')->result();
$data = $this->db->order_by('id', 'DESC')->get('kode_barang')->result();
$result = [];
$no = 1;
@@ -35,7 +35,10 @@ class Kodebarang extends CI_Controller {
$no++,
$row->kode_barang,
$row->nama,
$row->limit_stock,
(int)$row->limit_stock,
strtoupper($row->tracking_type ?? 'UNIT'),
strtolower($row->need_serial_number ?? 'false') === 'true' ? 'Ya' : 'Tidak',
$row->unit ?? 'PCS',
'
<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>
@@ -61,29 +64,57 @@ class Kodebarang extends CI_Controller {
// =========================
public function save()
{
$nama = $this->input->post('nama');
$kode = $this->input->post('kode');
$limit_stock = $this->input->post('limit_stock');
$nama = trim($this->input->post('nama'));
$kode = trim($this->input->post('kode'));
$limit_stock = (int)$this->input->post('limit_stock');
$tracking_type = strtoupper(trim($this->input->post('tracking_type')));
$need_serial_number = strtolower(trim($this->input->post('need_serial_number')));
$unit = trim($this->input->post('unit'));
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
if(!$nama || !$kode){
echo json_encode(['status'=>false,'message'=>'Kode dan nama wajib diisi']);
return;
}
if(!in_array($tracking_type, ['UNIT','QTY'])){
echo json_encode(['status'=>false,'message'=>'Tracking type tidak valid']);
return;
}
if(!in_array($need_serial_number, ['true','false'])){
echo json_encode(['status'=>false,'message'=>'Need serial number tidak valid']);
return;
}
if(!in_array($unit, ['PCS','Meter','Lembar'])){
echo json_encode(['status'=>false,'message'=>'Unit tidak valid']);
return;
}
$exists = $this->db->get_where('kode_barang', ['kode_barang' => $kode])->num_rows();
if($exists > 0){
echo json_encode(['status'=>false,'message'=>'Kode barang sudah digunakan']);
return;
}
$this->db->insert('kode_barang', [
'kode_barang' => $kode,
'nama' => $nama,
'limit_stock' => $limit_stock
'created_at' => date('Y-m-d H:i:s'),
'limit_stock' => $limit_stock,
'tracking_type' => $tracking_type,
'need_serial_number' => $need_serial_number,
'unit' => $unit
]);
log_activity(
'kode_barang',
'create',
'Menambahkan kode barang: ' . $nama,
'Menambahkan kode barang: ' . $kode . ' - ' . $nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
echo json_encode(['status'=>true,'message'=>'Kode barang berhasil ditambahkan']);
}
// =========================
@@ -91,30 +122,62 @@ class Kodebarang extends CI_Controller {
// =========================
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');
$id = (int)$this->input->post('id');
$kode = trim($this->input->post('kode'));
$nama = trim($this->input->post('nama'));
$limit_stock = (int)$this->input->post('limit_stock');
$tracking_type = strtoupper(trim($this->input->post('tracking_type')));
$need_serial_number = strtolower(trim($this->input->post('need_serial_number')));
$unit = trim($this->input->post('unit'));
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
if(!$id || !$nama || !$kode){
echo json_encode(['status'=>false,'message'=>'ID, kode dan nama wajib diisi']);
return;
}
if(!in_array($tracking_type, ['UNIT','QTY'])){
echo json_encode(['status'=>false,'message'=>'Tracking type tidak valid']);
return;
}
if(!in_array($need_serial_number, ['true','false'])){
echo json_encode(['status'=>false,'message'=>'Need serial number tidak valid']);
return;
}
if(!in_array($unit, ['PCS','Meter','Lembar'])){
echo json_encode(['status'=>false,'message'=>'Unit tidak valid']);
return;
}
$exists = $this->db
->where('kode_barang', $kode)
->where('id !=', $id)
->get('kode_barang')
->num_rows();
if($exists > 0){
echo json_encode(['status'=>false,'message'=>'Kode barang sudah digunakan']);
return;
}
$this->db->where('id',$id)->update('kode_barang', [
'kode_barang' => $kode,
'nama' => $nama,
'limit_stock' => $limit_stock
'limit_stock' => $limit_stock,
'tracking_type' => $tracking_type,
'need_serial_number' => $need_serial_number,
'unit' => $unit
]);
log_activity(
'kode_barang',
'update',
'Update kode barang ID: ' . $id . ' nama: ' . $nama,
'Update kode barang ID: ' . $id . ' (' . $kode . ' - ' . $nama . ')',
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
echo json_encode(['status'=>true,'message'=>'Kode barang berhasil diupdate']);
}
// =========================
@@ -122,13 +185,23 @@ class Kodebarang extends CI_Controller {
// =========================
public function delete($id)
{
// optional: cek relasi ke items
$cek = $this->db->get_where('items',['warehouse_id'=>$id])->num_rows();
$row = $this->db->get_where('kode_barang', ['id'=>$id])->row();
if(!$row){
echo json_encode([
'status'=>false,
'message'=>'Data kode barang tidak ditemukan'
]);
return;
}
// cek relasi ke items (mengacu ke kode_id)
$cek = $this->db->get_where('items',['kode_id'=>$id])->num_rows();
if($cek > 0){
echo json_encode([
'status'=>false,
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
'message'=>'Kode barang tidak bisa dihapus karena masih dipakai item'
]);
return;
}
@@ -138,11 +211,11 @@ class Kodebarang extends CI_Controller {
log_activity(
'kode_barang',
'delete',
'Hapus gudang ID: ' . $id,
'Hapus kode barang ID: ' . $id . ' (' . $row->kode_barang . ' - ' . $row->nama . ')',
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
echo json_encode(['status'=>true,'message'=>'Kode barang berhasil dihapus']);
}
+35 -68
View File
@@ -26,87 +26,54 @@ class Neracasaldo extends CI_Controller {
$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('
$tanggal_awal = $this->input->get('tanggal_awal');
$tanggal_akhir = $this->input->get('tanggal_akhir');
$this->db->select("
accounts.id,
accounts.kode_akun,
accounts.nama_akun,
accounts.kategori,
accounts.tipe,
accounts.posisi,
COALESCE(SUM(journal_details.debit),0) as debit,
COALESCE(SUM(journal_details.kredit),0) as kredit
');
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');
if (!empty($tanggal_awal) && !empty($tanggal_akhir)) {
$this->db->group_by('accounts.id');
$this->db->order_by('accounts.kode_akun','ASC');
$this->db->join(
"(SELECT jd.*
FROM journal_details jd
JOIN journals j ON j.id = jd.journal_id
WHERE j.tanggal BETWEEN ".$this->db->escape($tanggal_awal)."
AND ".$this->db->escape($tanggal_akhir)."
) journal_details",
"journal_details.account_id = accounts.id",
"left",
false
);
$rows = $this->db->get()->result();
} else {
// =========================
// HITUNG LABA BERJALAN
// =========================
$laba = $this->get_laba_berjalan();
$this->db->join(
'journal_details',
'journal_details.account_id = accounts.id',
'left'
);
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);
}
}
$rows = $this->db
->where('accounts.is_active', 1)
->group_by('accounts.id')
->order_by('accounts.kode_akun', 'ASC')
->get()
->result();
echo json_encode($rows);
}
}
+55 -45
View File
@@ -41,49 +41,55 @@ class PDF extends FPDF
function Header()
{
// Logo (opsional)
if (!empty($this->logo) && file_exists($this->logo)) {
$this->Image($this->logo, 10, 6, 18);
$this->SetXY(31, 10);
// Header lengkap hanya di halaman pertama
if ($this->PageNo() === 1) {
// Logo (opsional)
if (!empty($this->logo) && file_exists($this->logo)) {
$this->Image($this->logo, 10, 6, 18);
$this->SetXY(31, 10);
} else {
$this->SetXY(10, 10);
}
// Nama perusahaan
$this->SetFont('Arial', 'B', 12);
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
// Alamat
$this->SetFont('Arial', '', 9);
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
$this->SetX($x);
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
// Garis pemisah (A4 Portrait: x=10 s/d x=200)
$this->Ln(2);
$this->SetLineWidth(0.6);
$this->Line(10, $this->GetY(), 200, $this->GetY());
$this->SetLineWidth(0.2);
$this->Line(10, $this->GetY() + 1, 200, $this->GetY() + 1);
$this->Ln(5);
// Judul
$this->SetFont('Arial', 'B', 13);
$this->SetTextColor(245, 140, 0);
$this->Cell(0, 7, 'LAPORAN NERACA SALDO', 0, 1, 'C');
$this->SetTextColor(0, 0, 0);
// Periode
$this->SetFont('Arial', '', 10);
$this->Cell(0, 5, $this->periodeStr, 0, 1, 'C');
// Tanggal cetak
$this->SetFont('Arial', '', 9);
$this->Cell(0, 5, 'Di Cetak Pada : ' . date('d-m-Y') . ' - ' . date('H:i:s'), 0, 1, 'C');
$this->Ln(4);
} else {
$this->SetXY(10, 10);
// Halaman berikutnya tanpa header laporan
$this->SetY(10);
}
// Nama perusahaan
$this->SetFont('Arial', 'B', 12);
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
// Alamat
$this->SetFont('Arial', '', 9);
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
$this->SetX($x);
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
// Garis pemisah (A4 Portrait: x=10 s/d x=200)
$this->Ln(2);
$this->SetLineWidth(0.6);
$this->Line(10, $this->GetY(), 200, $this->GetY());
$this->SetLineWidth(0.2);
$this->Line(10, $this->GetY() + 1, 200, $this->GetY() + 1);
$this->Ln(5);
// Judul
$this->SetFont('Arial', 'B', 13);
$this->SetTextColor(245, 140, 0);
$this->Cell(0, 7, 'LAPORAN NERACA SALDO', 0, 1, 'C');
$this->SetTextColor(0, 0, 0);
// Periode
$this->SetFont('Arial', '', 10);
$this->Cell(0, 5, $this->periodeStr, 0, 1, 'C');
// Tanggal cetak
$this->SetFont('Arial', '', 9);
$this->Cell(0, 5, 'Di Cetak Pada : ' . date('d-m-Y') . ' - ' . date('H:i:s'), 0, 1, 'C');
$this->Ln(4);
// Header kolom tabel
// Header kolom tabel tetap tampil di setiap halaman
$this->_drawTableHeader();
}
@@ -146,8 +152,12 @@ class PDF extends FPDF
$this->Cell(array_sum($this->colWidths), 8, 'Tidak ada data', 1, 1, 'C');
} else {
foreach ($rows as $row) {
$namaAkun = isset($row->nama_akun) ? $row->nama_akun : (isset($row['nama_akun']) ? $row['nama_akun'] : '');
$kodeAkun = isset($row->kode_akun) ? $row->kode_akun : (isset($row['kode_akun']) ? $row['kode_akun'] : '');
$debit = isset($row->debit) ? (float)$row->debit : (isset($row['debit']) ? (float)$row['debit'] : 0);
$kredit = isset($row->kredit) ? (float)$row->kredit : (isset($row['kredit']) ? (float)$row['kredit'] : 0);
$nbLines = $this->NbLines($this->colWidths[1], $row['nama']);
$nbLines = $this->NbLines($this->colWidths[1], $namaAkun);
$rowH = max(1, $nbLines) * $lineH;
if ($this->GetY() + $rowH > 270) {
@@ -158,14 +168,14 @@ class PDF extends FPDF
$this->SetFont('Arial', '', 9);
$this->SetXY($startX, $y);
$this->Cell($this->colWidths[0], $rowH, $row['kode'], 1, 0, 'C');
$this->Cell($this->colWidths[0], $rowH, $kodeAkun, 1, 0, 'C');
$xNama = $this->GetX();
$this->MultiCell($this->colWidths[1], $lineH, $row['nama'], 1, 'L');
$this->MultiCell($this->colWidths[1], $lineH, $namaAkun, 1, 'L');
$this->SetXY($xNama + $this->colWidths[1], $y);
$this->Cell($this->colWidths[2], $rowH, $row['saldo_debit'] > 0 ? number_format($row['saldo_debit'], 0, ',', '.') : '-', 1, 0, 'R');
$this->Cell($this->colWidths[3], $rowH, $row['saldo_kredit'] > 0 ? number_format($row['saldo_kredit'], 0, ',', '.') : '-', 1, 0, 'R');
$this->Cell($this->colWidths[2], $rowH, $debit > 0 ? number_format($debit, 0, ',', '.') : '-', 1, 0, 'R');
$this->Cell($this->colWidths[3], $rowH, $kredit > 0 ? number_format($kredit, 0, ',', '.') : '-', 1, 0, 'R');
$this->SetXY($startX, $y + $rowH);
}
+308 -38
View File
@@ -16,9 +16,10 @@
<tr>
<th>No</th>
<th>Tanggal</th>
<th>Kode</th>
<th>Nama</th>
<th style="min-width: 80px;">Kode</th>
<th style="min-width: 80px;">Nama</th>
<th>Lokasi Asset</th>
<th style="max-width: 150px;">Keterangan</th>
<th>Nilai</th>
<th>Masa</th>
<th>Susut/Bulan</th>
@@ -56,9 +57,10 @@
<input type="text" id="nama_asset" class="form-control">
<label class="mt-2">Keterangan</label>
<input type="text" id="keterangan" class="form-control">
<textarea rows="3" id="keterangan" class="form-control"></textarea>
<!-- <input type="text" id="keterangan" class="form-control"> -->
<label class="mt-2">Harga Per Unit</label>
<label class="mt-2">Harga</label>
<input type="text" id="harga" class="form-control format-rupiah">
<label class="mt-2">Tanggal</label>
@@ -99,7 +101,8 @@
<select id="edit_lokasi_asset" class="form-control"></select>
<label class="mt-2">Keterangan</label>
<input type="text" id="edit_keterangan" class="form-control">
<!-- <input type="text" id="edit_keterangan" class="form-control"> -->
<textarea rows="3" id="edit_keterangan" class="form-control"></textarea>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="btnUpdate">Update</button>
@@ -146,7 +149,7 @@
</div>
<div class="modal-body">
<input type="hidden" id="s_item_id">
<input type="hidden" id="s_harga">
<!-- <input type="hidden" id="s_harga"> -->
<label class="mt-2">Akun Asset (Debit)</label>
<select id="s_account_asset" class="form-control"></select>
@@ -155,14 +158,33 @@
<select id="s_lokasi_asset" class="form-control"></select>
<label>Pilih Barang</label>
<select id="s_item" class="form-control"></select>
<select id="s_item" class="form-control select-search"></select>
<label class="mt-2">Stok Tersedia</label>
<input type="text" id="s_stok" class="form-control" readonly>
<div class="mb-2 d-none" id="wrap_barcode_select">
<label for="barcode_select" class="form-label">Barcode / QR ID</label>
<select id="barcode_select" class="form-control select-search"></select>
</div>
<div id="stokSisaQty"></div>
<label class="mt-2">Harga Barang</label>
<input type="text" id="s_harga" class="form-control format-rupiah" disabled>
<div class="mb-0 d-none" id="wrap_qty">
<label class="mt-2">Qty</label>
<input type="number" id="s_qty" placeholder="1" class="form-control">
<label class="mt-2">Total Harga</label>
<input type="text" id="s_total_harga" class="form-control format-rupiah" disabled>
</div>
<label class="mt-2">Tanggal</label>
<input type="date" id="s_tanggal" class="form-control">
<label class="mt-2">Keterangan</label>
<textarea rows="3" id="s_keterangan" class="form-control"></textarea>
<!-- <input type="text" id="s_keterangan" class="form-control"> -->
<hr>
<label>Masa Manfaat (bulan)</label>
@@ -224,6 +246,10 @@ function formatRupiah(angka){
return angka.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
function parseNumber(val){
return parseInt((val || '').toString().replace(/\D/g,'')) || 0;
}
function setSelectValue(selector, value){
setTimeout(()=>{
$(selector).val(value).trigger('change');
@@ -269,7 +295,7 @@ function loadItems(){
$.get("<?= base_url('asset/get_items'); ?>",res=>{
let opt='<option value="">-- PILIH BARANG --</option>';
res.forEach(i=>{
opt += `<option value="${i.id}" data-harga="${i.harga_beli}" data-stok="${i.stok}" data-nama="${i.nama_barang}">
opt += `<option value="${i.id}" data-id="${i.id}" data-harga="${i.harga_beli}" data-stok="${i.stok}" data-nama="${i.nama_barang}" data-type="${i.tracking_type}">
${i.kode_detail} - ${i.nama_barang} - ( Stok: ${i.stok})
</option>`;
});
@@ -282,20 +308,81 @@ function hitungSusut(){
let harga = parseInt($('#harga').val().replace(/\D/g,'')) || 0;
let residu = parseInt($('#residu').val().replace(/\D/g,'')) || 0;
let masa = parseInt($('#masa').val()) || 1;
let maxResidu = harga / 100 * 80;
if (residu > maxResidu) {
residu = maxResidu;
$('#residu').val(maxResidu).trigger('input');
}
let susut = (harga - residu) / masa;
$('#susut').val(formatRupiah(Math.round(susut)));
susut = Math.max(0, susut);
$('#susut').val(susut);
}
// ================= LOAD BARCODE ITEMS =================
// function loadItemBarcodes(item_id){
// return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
// if(res && res.length){
// let opt = '<option value="">Pilih Item</option>';
// res.forEach(i=>{
// opt += `<option value="${i.barcode_id}">
// ${i.nama_barang} - ${i.barcode} - ${i.serial_number}
// </option>`;
// });
// $('#barcode_select').html(opt);
// $('#wrap_barcode_select').removeClass('d-none');
// } else {
// $('#wrap_barcode_select').addClass('d-none');
// }
// }, 'json');
// }
function loadItemBarcodes(item_id, selectedBarcode = null){
return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
if(res && res.length){
let opt = '<option value="">Pilih Barcode</option>';
res.forEach(i=>{
opt += `
<option value="${i.barcode_id}" data-qty_sisa="${i.qty_sisa}">
${i.nama_barang} - ${i.barcode} - ${i.serial_number}
</option>
`;
});
$('#barcode_select').html(opt);
if(selectedBarcode){
$('#barcode_select').val(selectedBarcode);
}
$('#wrap_barcode_select').removeClass('d-none');
// setMode('item');
}else{
$('#wrap_barcode_select').addClass('d-none');
// setMode('manual');
}
},'json');
}
$('#masa, #residu, #harga').on('keyup change',hitungSusut);
// ================= HITUNG SUSUT STOCK =================
$('#s_masa, #s_residu').on('keyup change',function(){
let harga = parseInt($('#s_harga').val()) || 0;
let residu = parseInt($('#s_residu').val().replace(/\D/g,'')) || 0;
function hitungSusutStock(){
let trackingType = ($('#s_item option:selected').data('type') || '').toString().trim().toUpperCase();
let hargaDasar = trackingType === 'QTY' ? parseNumber($('#s_total_harga').val()) : parseNumber($('#s_harga').val());
let residu = parseNumber($('#s_residu').val());
let masa = parseInt($('#s_masa').val()) || 1;
let susut = (harga - residu) / masa;
$('#s_susut').val(formatRupiah(Math.round(susut)));
});
let maxResidu = hargaDasar / 100 * 80;
if (residu > maxResidu) {
residu = maxResidu;
$('#s_residu').val(maxResidu).trigger('input');
}
let susut = (hargaDasar - residu) / masa;
susut = Math.max(0, susut);
$('#s_susut').val(susut);
}
$('#s_masa, #s_residu').on('keyup change', hitungSusutStock);
// ================= TAMBAH ASSET MANUAL =================
$('.btn-add-asset').click(function(){
@@ -303,26 +390,77 @@ $('.btn-add-asset').click(function(){
$('#modalTitleAsset').text('Tambah Asset');
$('#modalAsset input[type="text"], #modalAsset input[type="date"], #modalAsset input[type="number"], #modalAsset select').val('');
$('#id_asset').val('');
$('#stokSisaQty').html('');
loadKas(); loadAsset(); loadLokasiAsset();
$('#modalAsset').modal('show');
});
// ================= DETAIL & HISTORY =================
$(document).on('click','.btn-detail',function(){
let id = $(this).data('id');
$.get("<?= base_url('asset/detail/'); ?>"+id, function(asset){
$('#detailContent').html(`
<table class="table table-borderless">
<tr><td><strong>Kode Asset</strong></td><td>: <strong>${asset.kode_asset || '-'}</strong></td></tr>
<tr><td><strong>Nama Asset</strong></td><td>: <strong>${asset.nama_asset}</strong></td></tr>
<tr><td><strong>Lokasi</strong></td><td>: <strong>${asset.nama_lokasi || '-'}</strong></td></tr>
<tr><td><strong>Harga/Unit</strong></td><td>: <strong>Rp ${formatRupiah(asset.harga_per_unit)}</strong></td></tr>
<tr><td><strong>Nilai Buku</strong></td><td>: <strong>Rp ${formatRupiah(asset.nilai_buku)}</strong></td></tr>
<tr><td><strong>Masa Manfaat</strong></td><td>: <strong>${asset.masa_manfaat} bulan</strong></td></tr>
<tr><td><strong>Sumber</strong></td><td>: <strong>${asset.sumber}</strong></td></tr>
<tr><td><strong>Keterangan</strong></td><td>: <strong>${asset.keterangan || '-'}</strong></td></tr>
</table>
<div class="row">
<div class="col-md-6">
<table class="table table-borderless table-sm">
<tr>
<td width="45%"><strong>Kode Asset</strong></td>
<td>: <strong>${asset.kode_asset || '-'}</strong></td>
</tr>
<tr>
<td><strong>Nama Asset</strong></td>
<td>: <strong>${asset.nama_asset}</strong></td>
</tr>
<tr>
<td><strong>Lokasi</strong></td>
<td>: <strong>${asset.nama_lokasi || '-'}</strong></td>
</tr>
<tr>
<td><strong>Harga/Unit</strong></td>
<td>: <strong>Rp ${formatRupiah(asset.harga_per_unit)}</strong></td>
</tr>
<tr>
<td><strong>Qty</strong></td>
<td>: <strong>${asset.qty}</strong></td>
</tr>
<tr>
<td><strong>Nilai Perolehan</strong></td>
<td>: <strong>Rp ${formatRupiah(asset.nilai_perolehan)}</strong></td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-borderless table-sm">
<tr>
<td width="45%"><strong>Akumulasi Penyusutan</strong></td>
<td>: <strong>Rp ${formatRupiah(asset.akumulasi_penyusutan || 0)}</strong></td>
</tr>
<tr>
<td><strong>Nilai Buku</strong></td>
<td>: <strong>Rp ${formatRupiah(asset.nilai_buku)}</strong></td>
</tr>
<tr>
<td><strong>Masa Manfaat</strong></td>
<td>: <strong>${asset.masa_manfaat} bulan</strong></td>
</tr>
<tr>
<td><strong>Sumber</strong></td>
<td>: <strong>${asset.sumber}</strong></td>
</tr>
<tr>
<td><strong>Keterangan</strong></td>
<td>: <strong>${asset.keterangan || '-'}</strong></td>
</tr>
</table>
</div>
</div>
`);
// Load history
@@ -336,7 +474,7 @@ $(document).on('click','.btn-detail',function(){
h.tipe=='penambahan' ? 'bg-info' :
h.tipe=='pengurangan' ? 'bg-warning' : 'bg-secondary';
rows += `<tr>
<td>${h.created_at}</td>
<td>${h.tanggal_dibuat}</td>
<td><span class="badge ${badgeClass}">${h.tipe}</span></td>
<td>Rp ${formatRupiah(h.nilai || 0)}</td>
<td>${h.keterangan}</td>
@@ -465,35 +603,157 @@ $('#btnSave').click(function(){
});
// ================= FROM STOCK =================
$('.btn-from-stock').click(function(){
function resetStockForm(){
$('#modalAssetStock input[type="text"], #modalAssetStock input[type="date"], #modalAssetStock input[type="number"], #modalAssetStock select').val('');
$('#s_item_id, #s_harga').val('');
$('#s_item_id, #s_harga, #s_total_harga, #s_susut, #s_keterangan').val('');
$('#s_qty').val(1);
$('#stokSisaQty').html('');
$('#wrap_qty').addClass('d-none');
$('#wrap_barcode_select').removeClass('d-none');
$('#barcode_select').html('<option value="">Loading...</option>');
}
$('.btn-from-stock').click(function(){
resetStockForm();
loadItems();
loadAsset();
loadLokasiAsset();
$('#modalAssetStock').modal('show');
});
// ================= PILIH ITEM STOCK =================
$(document).on('change','#s_item',function(){
$('#barcode_select').on('change', function(){
let selectedItem = $('#s_item option:selected');
let type = (selectedItem.data('type') || '').toString().trim().toUpperCase();
let selected = $(this).find(':selected');
let harga = selected.data('harga') || 0;
let stok = selected.data('stok') || 0;
$('#s_item_id').val($(this).val());
let qty_sisa = parseFloat(selected.data('qty_sisa')) || 0;
if($(this).val()){
$('#stokSisaQty').html('<h6 class="text-success ml-4">Stok : ' + qty_sisa + '</h6><hr>');
}else{
$('#stokSisaQty').html('');
}
// perlakuan qty setelah barcode dipilih, berdasarkan tracking_type
if(type === 'UNIT'){
$('#stokSisaQty').html('');
$('#wrap_qty').addClass('d-none');
$('#s_qty').removeAttr('max').val(1);
$('#s_total_harga').val(parseNumber($('#s_harga').val()));
hitungSusutStock();
}else{
$('#wrap_qty').removeClass('d-none');
$('#s_qty').attr('min', 1).attr('max', qty_sisa > 0 ? qty_sisa : 1).val('');
$('#s_total_harga').val('');
}
});
$('#s_item').on('change', function(){
let selected = $(this).find(':selected');
let item_id = selected.data('id');
let type = selected.data('type');
let harga = parseInt(selected.data('harga')) || 0;
let stok = parseFloat(selected.data('stok')) || 0;
$('#s_item_id').val(item_id || '');
$('#s_harga').val(harga);
$('#s_stok').val(stok);
$('#s_total_harga').val(harga);
$('#stokSisaQty').html('');
$('#s_qty').val(1).removeAttr('max');
$('#wrap_qty').addClass('d-none');
$('#wrap_barcode_select').addClass('d-none');
$('#barcode_select').html('<option value="">Pilih Barcode</option>');
if(!item_id){
return;
}
let normalizedType = (type || '').toString().trim().toUpperCase();
// barcode tetap harus muncul untuk ITEM maupun QTY
loadItemBarcodes(item_id, null).done(function(res){
if(res && res.length){
$('#wrap_barcode_select').removeClass('d-none');
}else{
$('#wrap_barcode_select').addClass('d-none');
$('#barcode_select').html('<option value="">Barcode tidak tersedia</option>');
}
// sebelum pilih barcode: qty & total belum ditentukan
$('#wrap_qty').addClass('d-none');
$('#s_qty').removeAttr('max').val('');
$('#s_total_harga').val('');
}).fail(function(){
$('#wrap_barcode_select').addClass('d-none');
$('#barcode_select').html('<option value="">Gagal load barcode</option>');
$('#wrap_qty').addClass('d-none');
$('#s_qty').removeAttr('max').val('');
$('#s_total_harga').val('');
});
});
$('#s_qty').on('keyup change',function(){
let selectedItem = $('#s_item option:selected');
let type = (selectedItem.data('type') || '').toString().trim().toUpperCase();
let qtyRaw = $('#s_qty').val();
let qty = parseInt(qtyRaw);
let harga = parseNumber($('#s_harga').val());
let max = parseFloat($('#s_qty').attr('max')) || 0;
if(type === 'UNIT'){
$('#wrap_qty').addClass('d-none');
$('#s_qty').val(1);
$('#s_total_harga').val(harga);
return;
}
if(!qtyRaw || isNaN(qty) || qty < 1){
$('#s_total_harga').val('');
return;
}
if(max > 0 && qty > max){
qty = max;
Swal.fire('Warning','Stok tidak cukup! Max: ' + max,'warning');
}
let total = harga * qty;
$('#s_qty').val(qty);
$('#s_total_harga').val(total);
hitungSusutStock();
});
// ================= SAVE FROM STOCK =================
$('#btnSaveStock').click(function(){
let btn = $(this);
let trackingType = ($('#s_item option:selected').data('type') || '').toString().trim().toUpperCase();
let maxQty = parseFloat($('#s_qty').attr('max')) || 0;
let qtyRaw = $('#s_qty').val();
let qty = parseInt(qtyRaw);
if(trackingType === 'UNIT'){
qty = 1;
}else{
if(!qtyRaw || isNaN(qty) || qty < 1){
Swal.fire('Error','Qty wajib diisi untuk item tracking QTY','error');
return;
}
if(maxQty > 0 && qty > maxQty){
Swal.fire('Error','Qty melebihi stok tersedia','error');
return;
}
}
let data = {
item_id: $('#s_item').val(),
s_tanggal: $('#s_tanggal').val(),
s_masa: $('#s_masa').val(),
s_residu: $('#s_residu').val(),
s_lokasi_asset: $('#s_lokasi_asset').val(),
account_asset: $('#s_account_asset').val()
account_asset: $('#s_account_asset').val(),
s_keterangan: $('#s_keterangan').val(),
barcode_id: $('#barcode_select').val(),
qty: qty
};
// Validasi
@@ -502,6 +762,15 @@ $('#btnSaveStock').click(function(){
return;
}
if(!data.barcode_id){
Swal.fire('Error','Barcode wajib dipilih','error');
return;
}
if(trackingType === 'UNIT'){
data.qty = 1;
}
btn.prop('disabled', true).html('Menyimpan...');
$.post("<?= base_url('asset/save_from_stock'); ?>",data,function(res){
@@ -523,5 +792,6 @@ $('#btnSaveStock').click(function(){
});
});
});
</script>
+82 -83
View File
@@ -122,10 +122,12 @@
</div>
<div class="mb-2 d-none" id="wrap_barcode_select">
<label for="barcode_select" class="form-label">Barcode ID</label>
<label for="barcode_select" class="form-label">Barcode / QR ID</label>
<select id="barcode_select" class="form-control select-search"></select>
</div>
<div id="stokSisaQty"></div>
<div class="mb-2" id="wrap_qty_item">
<label for="qty" class="form-label">Qty</label>
<input type="number" id="qty" class="form-control" min="0">
@@ -232,6 +234,7 @@ $(function(){
$('#wrap_item_select').addClass('d-none');
$('#wrap_nama_item').removeClass('d-none');
$('#wrap_qty_item').removeClass('d-none');
$('#wrap_barcode_select').addClass('d-none');
$('#warehouse_id').html('<option value="">Pilih Gudang</option>');
$('#item_select').html('<option value="">Pilih Item</option>');
@@ -241,10 +244,12 @@ $(function(){
$('#qty').val('');
$('#harga').val('');
$('#keterangan').val('');
$('#barcode_select').val('');
$('#is_cicilan').prop('checked', false);
$('#wrap_cicilan').addClass('d-none');
$('#tenor').val('');
$('#stokSisaQty').html('');
setMode('manual');
}
@@ -359,33 +364,91 @@ $(function(){
return;
}
loadItemBarcodes(item_id);
// loadItemBarcodes(item_id);
loadItemBarcodes(
item_id,
isEditing ? editData.barcode_id : null
);
});
$('#barcode_select').on('change', function(){
let barcode_id = $(this).val();
let selected = $(this).find(':selected');
let qty_sisa = parseFloat(selected.data('qty_sisa')) || 0;
// console.log("QTY : " + qty_sisa);
if (qty_sisa > 1 ) {
$('#stokSisaQty').html('<h6 class="text-success ml-4">Stok : ' + qty_sisa + '</h6><hr>');
$('#qty').val('');
$('#wrap_qty_item').removeClass('d-none');
}
});
// $(document).on('keyup','#qty',function(){
// hitungTotal();
// });
// function hitungTotal(){
// let qty_sisa = 1000;
// $('#stokSisaQty').html('<h6 class="text-success ml-4">Stok : ' + qty_sisa + '</h6><hr>');
// }
// ================= LOAD BARCODE ITEMS =================
function loadItemBarcodes(item_id){
// function loadItemBarcodes(item_id){
// return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
// if(res && res.length){
// let opt = '<option value="">Pilih Item</option>';
// res.forEach(i=>{
// opt += `<option value="${i.barcode_id}">
// ${i.nama_barang} - ${i.barcode} - ${i.serial_number}
// </option>`;
// });
// $('#barcode_select').html(opt);
// $('#wrap_barcode_select').removeClass('d-none');
// // set QTY jadi 1
// let qty = Number($('#qty').val());
// $('#qty').val(qty + 1);
// setMode('item');
// } else {
// $('#wrap_barcode_select').addClass('d-none');
// // $('#wrap_barcode_select').removeClass('d-none');
// setMode('manual');
// }
// }, 'json');
// }
function loadItemBarcodes(item_id, selectedBarcode = null){
return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
if(res && res.length){
let opt = '<option value="">Pilih Item</option>';
let opt = '<option value="">Pilih Barcode</option>';
res.forEach(i=>{
opt += `<option value="${i.barcode_id}">
${i.nama_barang} - ${i.barcode} - ${i.serial_number}
</option>`;
opt += `
<option value="${i.barcode_id}" data-qty_sisa="${i.qty_sisa}">
${i.nama_barang} - ${i.barcode} - ${i.serial_number}
</option>
`;
});
$('#barcode_select').html(opt);
if(selectedBarcode){
$('#barcode_select').val(selectedBarcode);
}
$('#wrap_barcode_select').removeClass('d-none');
// set QTY jadi 1
let qty = Number($('#qty').val());
$('#qty').val(qty + 1);
if(!isEditing){
$('#qty').val(1);
}
setMode('item');
} else {
}else{
$('#wrap_barcode_select').addClass('d-none');
// $('#wrap_barcode_select').removeClass('d-none');
setMode('manual');
}
}, 'json');
},'json');
}
// ================= AUTO HARGA =================
@@ -398,8 +461,8 @@ $(function(){
// ================= STOK CHECK =================
$('#qty').on('input', function(){
let qty = parseFloat($(this).val()) || 0;
let selected = $('#item_select option:selected');
let stok = selected.length ? parseFloat(selected.data('stok')) || 0 : 0;
let selected = $('#barcode_select option:selected');
let stok = selected.length ? parseFloat(selected.data('qty_sisa')) || 0 : 0;
if(stok > 0 && qty > stok){
Swal.fire('Warning','Stok tidak cukup! Max: ' + stok,'warning');
@@ -527,25 +590,7 @@ $(function(){
let btnAction = '';
if(v.status == 'draft'){
let btnEditTable = '';
if(v.is_cicilan == 0) {
btnEditTable = `
<button class="btn btn-sm btn-secondary btn-editItem"
data-id="${v.id}"
data-tanggal_input="${v.tanggal}"
data-account="${v.account_id}"
data-item="${v.nama_item}"
data-qty="${v.qty}"
data-harga="${v.harga}"
data-warehouse="${v.warehouse_id || ''}"
data-item_id="${v.items_id || ''}"
data-keterangan="${v.keterangan || ''}"
data-is_cicilan="${v.is_cicilan || 0}">
Edit
</button>`;
}
btnAction = `
${btnEditTable}
<button class="btn btn-sm btn-danger btn-delete" data-id="${v.id}">
Hapus
</button>
@@ -614,53 +659,6 @@ $(function(){
modal.show();
});
// ================= EDIT =================
$(document).on('click','.btn-editItem', function(){
action = "edit";
edit_id = $(this).data('id');
isEditing = true;
editData = $(this).data();
$('#modalTitle').text('Edit Item');
resetForm();
// Set basic fields first
$('#account_id').val(editData.account).trigger('change');
// Wait for account change to complete, then set other fields
setTimeout(() => {
if(editData.warehouse){
$('#warehouse_id').val(editData.warehouse).trigger('change');
// gak ada checkbox cicilan
$('#is_cicilan_group').addClass('d-none');
// Wait for warehouse items to load
setTimeout(() => {
$('#item_select').val(editData.item_id).trigger('change');
}, 500);
} else {
$('#nama_item').val(editData.item);
}
$('#qty').val(editData.qty);
$('#harga').val(editData.harga);
$('#tanggal_input').val(editData.tanggal_input);
$('#keterangan').val(editData.keterangan);
}, 500);
// if(editData.is_cicilan == 1){
// $('#qty').prop('disabled', true);
// $('#harga').prop('disabled', true);
// $('#is_cicilan').prop('disabled', true);
// $('#tenor').prop('disabled', true);
// }
modal.show();
});
// ================= SAVE =================
// ================= SAVE - FIXED SESUAI STYLE ANDA =================
@@ -693,6 +691,7 @@ $(function(){
qty: parseFloat($('#qty').val()) || 0,
harga: parseFloat($('#harga').val()) || 0,
keterangan: $('#keterangan').val().trim(),
barcode_id: $('#barcode_select').val() || 0,
is_cicilan: $('#is_cicilan').is(':checked') ? 1 : 0,
tenor: parseInt($('#tenor').val()) || 0,
@@ -757,7 +756,7 @@ $(function(){
btn.prop('disabled', true).html('Menghapus...');
$.get({
url: "<?= base_url('invoices/delete_item/'); ?>" + id,
url: "<?= base_url('invoices/delete_item/'); ?>" + id + "/" + `<?= $no_invoice ?>`,
dataType: 'json' // ✅ FIXED
})
.done(function(res){
+339 -322
View File
@@ -34,7 +34,7 @@
<!-- MODAL -->
<div class="modal fade" id="modalItem">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
@@ -45,44 +45,146 @@
<input type="hidden" id="id">
<div id="group_tanggal">
<label>Tanggal Pembelian</label>
<input type="date" id="tanggal_beli" class="form-control">
</div>
<div id="group_kode">
<label class="mt-2">Kode Barang</label>
<select id="kode_barang" class="form-control select-search"></select>
<div class="row">
<div class="col-md-6">
<div id="group_tanggal">
<label>Tanggal Pembelian</label>
<input type="date" id="tanggal_beli" class="form-control">
</div>
</div>
<div class="col-md-6">
<div id="group_kode">
<label class="mt-2">Kode Barang</label>
<select id="kode_barang" class="form-control select-search"></select>
</div>
</div>
</div>
<label>Nama Barang</label>
<input type="text" id="nama_barang" class="form-control">
<div id="group_qty">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty" class="form-control">
<div class="row">
<div class="col-md-2">
<div id="group_qty">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty" class="form-control">
</div>
</div>
<div class="col-md-2">
<div id="group_tracking_qty">
<label class="mt-2">Jml / (Roll, Box, Dus)</label>
<input type="number" id="tracking_qty" class="form-control">
</div>
</div>
<div class="col-md-4">
<label class="mt-2">Harga Beli</label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" id="harga_beli" class="form-control format-rupiah">
</div>
</div>
<div class="col-md-4">
<label class="mt-2">Harga Jual</label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" id="harga_jual" class="form-control format-rupiah">
</div>
</div>
</div>
<div id="group_tracking_qty">
<label class="mt-2">Jumlah Per (Roll, Box, Dus)</label>
<input type="number" id="tracking_qty" class="form-control">
</div>
<label class="mt-2">Harga Beli</label>
<input type="text" id="harga_beli" class="form-control format-rupiah">
<label class="mt-2">Harga Jual</label>
<input type="text" id="harga_jual" class="form-control format-rupiah">
<div id="group_warehouse">
<label class="mt-2">Gudang</label>
<select id="warehouse_id" class="form-control"></select>
</div>
<div id="group_account">
<!-- <div class="row">
<label class="mt-2">Akun Kas</label>
<select id="account_kas" class="form-control"></select>
<div class="col-md-7">
<div id="group_account">
<select id="account_kas" class="form-control"></select>
</div>
</div>
<div class="col-md-5">
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" id="account[]" class="form-control format-rupiah">
</div>
</div>
</div>
<button class="btn btn-warning btn-sm mt-4">Tambah Account</button> -->
<hr>
<label class="font-weight-bold">
Pembayaran
</label>
<div id="accountContainer">
<div class="row account-row mb-2">
<div class="col-md-6">
<select
name="account_kas[]"
class="form-control account_kas">
</select>
</div>
<div class="col-md-4">
<div class="input-group">
<span class="input-group-text">Rp</span>
<input
type="text"
name="nominal[]"
class="form-control nominal format-rupiah">
</div>
</div>
<div class="col-md-2">
<button
type="button"
class="btn btn-sm btn-danger btn-remove-account w-100"
disabled>
Hapus
</button>
</div>
</div>
</div>
<div class="mt-2">
<button
type="button"
id="btnTambahAccount"
class="btn btn-warning btn-sm">
+ Tambah Account
</button>
</div>
<div class="mt-3">
<div class="row">
<div class="col-md-4">
<!-- total qty * harga beli -->
<h5>
Total :
<span id="totalHppBarang">
Rp 0
</span>
</h5>
</div>
<div class="col-md-4">
<!-- total accounting -->
<h5>
Total :
<span id="totalAccount">
Rp 0
</span>
</h5>
</div>
<div class="col-md-4">
<!-- balance accounting + total hpp barang -->
<h5>
Total :
<span id="totalAll">
Rp 0
</span>
</h5>
</div>
</div>
</div>
</div>
@@ -94,59 +196,6 @@
</div>
</div>
<!-- MODAL BARANAG KELUAR -->
<div class="modal fade" id="modalKeluar">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5>Barang Keluar</h5>
</div>
<div class="modal-body">
<div id="group_account_biaya">
<label class="mt-2">Akun Biaya</label>
<select id="account_biaya" class="form-control"></select>
</div>
<div>
<label>Tanggal</label>
<input type="date" id="tanggal_keluar" class="form-control">
</div>
<div>
<label class="mt-2">Gudang</label>
<select id="warehouse_id_keluar" class="form-control"></select>
</div>
<div class="d-none" id="group_barang_keluar">
<label class="mt-2">Barang</label>
<select id="barang_id" class="form-control select-search"></select>
</div>
<div class="d-none" id="group_qty_keluar">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty_keluar" class="form-control">
</div>
<div class="mb-2 d-none" id="group_keterangan">
<label for="keterangan" class="form-label">Keterangan</label>
<textarea class="form-control" id="keterangan_keluar" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger" id="btnKeluar">Keluarkan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL -->
<div class="modal fade" id="modalDetail">
<div class="modal-dialog modal-lg">
@@ -163,46 +212,13 @@
</div>
<div class="modal fade" id="modalAdjust">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning">
<h5>Penyesuaian Stok</h5>
</div>
<div class="modal-body">
<input type="hidden" id="adjust_item_id">
<label>Qty Adjustment</label>
<input type="number" id="adjust_qty" class="form-control">
<small>Gunakan minus (-) jika mengurangi</small>
<label class="mt-2">Gudang</label>
<select id="adjust_warehouse" class="form-control"></select>
<label class="mt-2">Keterangan</label>
<input type="text" id="adjust_ket" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnAdjust">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let action = 'add';
let selectedItemId = null;
let accountOption = '';
// ================= DATATABLE =================
let table = $('#tableItems').DataTable({
@@ -215,24 +231,26 @@ let table = $('#tableItems').DataTable({
});
// ================= LOAD AKUN KAS =================
// function loadKas(){
// $.get("<?= base_url('items/get_accounts'); ?>",res=>{
// let opt='<option value="">-- PILIH KAS --</option>';
// res.forEach(a=>{
// opt += `<option value="${a.id}">${a.nama_akun}</option>`;
// });
// $('#account_kas').html(opt);
// },'json');
// }
function loadKas(){
$.get("<?= base_url('items/get_accounts'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
$.get("<?= base_url('items/get_accounts'); ?>",function(res){
accountOption = '<option value="">-- PILIH KAS --</option>';
res.forEach(function(a){
accountOption += `
<option value="${a.id}">
${a.nama_akun}
</option>
`;
});
$('#account_kas').html(opt);
},'json');
}
// ================= LOAD AKUN KAS =================
function loadKasOut(){
$.get("<?= base_url('items/get_accounts_biaya'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
});
$('#account_biaya').html(opt);
$('.account_kas').html(accountOption);
},'json');
}
@@ -246,26 +264,6 @@ function loadGudang(){
$('#warehouse_id, #adjust_warehouse').html(opt);
},'json');
}
// ================= LOAD GUDANG =================
function loadGudangList(){
$.get("<?= base_url('warehouses/list'); ?>",res=>{
let opt='<option value="">-- PILIH GUDANG --</option>';
res.forEach(w=>{
opt += `<option value="${w.id}">${w.nama}</option>`;
});
$('#warehouse_id_keluar').html(opt);
},'json');
}
// function loadKode(){
// $.get("<?= base_url('kodebarang/list'); ?>",res=>{
// let opt='<option value="">-- PILIH KODE --</option>';
// res.forEach(a=>{
// opt += `<option value="${a.id}" data-type="${a.tracking_type}">${a.kode_barang} - ${a.nama}</option>`;
// });
// $('#kode_barang').html(opt);
// },'json');
// }
function loadKode() {
$.get("<?= base_url('kodebarang/list'); ?>", function(res) {
@@ -285,6 +283,40 @@ function loadKode() {
}, 'json');
}
function addAccountRow(){
let html = `
<div class="row account-row mb-2">
<div class="col-md-6">
<select
name="account_kas[]"
class="form-control account_kas">
${accountOption}
</select>
</div>
<div class="col-md-4">
<div class="input-group">
<span class="input-group-text">
Rp
</span>
<input
type="text"
name="nominal[]"
class="form-control nominal format-rupiah">
</div>
</div>
<div class="col-md-2">
<button
type="button"
class="btn btn-sm btn-danger btn-remove-account w-100">
Hapus
</button>
</div>
</div>
`;
$('#accountContainer').append(html);
hitungTotal();
}
$(document).on('change', '#kode_barang', function () {
let type = $(this).find(':selected').data('type');
@@ -298,6 +330,39 @@ $(document).on('change', '#kode_barang', function () {
});
$('#btnTambahAccount').click(function(){
addAccountRow();
});
$(document).on('click','.btn-remove-account',function(){
$(this).closest('.account-row').remove();
hitungTotal();
});
// function hitungTotal(){
// let total = 0;
// $('.nominal').each(function(){
// let angka = $(this)
// .val()
// .replace(/\./g,'')
// .replace(/,/g,'');
// total += Number(angka || 0);
// });
// $('#totalAccount').text(formatRupiah(total.toString()));
// }
$(document).on('keyup','.nominal',function(){
hitungTotal();
});
$(document).on('keyup','#harga_beli',function(){
hitungTotal();
});
$(document).on('keyup change','#qty',function(){
hitungTotal();
});
// ================= RESET FORM =================
function resetForm(){
$('#id').val('');
@@ -308,6 +373,42 @@ function resetForm(){
$('#tanggal_beli').val('');
$('#tracking_qty').val('');
$('#group_tracking_qty').hide();
$('#accountContainer').html(`
<div class="row account-row mb-2">
<div class="col-md-6">
<select
name="account_kas[]"
class="form-control account_kas">
${accountOption}
</select>
</div>
<div class="col-md-4">
<div class="input-group">
<span class="input-group-text">Rp</span>
<input
type="text"
name="nominal[]"
class="form-control nominal format-rupiah">
</div>
</div>
<div class="col-md-2">
<button
class="btn btn-sm btn-danger btn-remove-account w-100"
disabled>
Hapus
</button>
</div>
</div>
`);
$('#totalAccount').text('Rp 0');
$('#totalHppBarang').text('Rp 0');
$('#totalAll')
.text('Balance')
.removeClass('text-danger')
.addClass('text-success');
$('#btnSimpan').prop('disabled',true);
}
// ================= INIT LOAD =================
@@ -362,6 +463,40 @@ $('#btnSimpan').click(function(){
let btn = $(this);
let qtys = parseFloat($('#qty').val()) || 0;
let totalHpp = qtys * rupiahToNumber($('#harga_beli').val());
let totalAccount = 0;
$('.nominal').each(function(){
totalAccount += rupiahToNumber($(this).val());
});
if(totalHpp != totalAccount){
Swal.fire({
icon:'warning',
title:'Belum Balance',
text:'Total pembayaran harus sama dengan Total Harga Beli.'
});
return;
}
let account = [];
$('.account-row').each(function(){
account.push({
account_id: $(this).find('.account_kas').val(),
nominal: $(this)
.find('.nominal')
.val()
});
});
// data.accounts = account;
let data = {
id: $('#id').val(),
kode_id: $('#kode_barang').val(),
@@ -370,7 +505,8 @@ $('#btnSimpan').click(function(){
harga_beli: $('#harga_beli').val(),
harga_jual: $('#harga_jual').val(),
warehouse_id: $('#warehouse_id').val(),
account_kas: $('#account_kas').val(),
// account_kas: $('#account_kas').val(),
account_kas: account,
tanggal_beli: $('#tanggal_beli').val(),
tracking_qty: $('#tracking_qty').val(),
status: `draft`
@@ -423,122 +559,6 @@ $('#btnSimpan').click(function(){
});
// =====================================================
// ================= START URUSAN KELUAR ===============
// =====================================================
// ================= RESET FORM KELUAR =================
function resetFormKeluar(){
$('#tanggal_keluar').val('');
$('#qty_keluar').val('');
$('#keterangan_keluar').val('');
}
loadKasOut();
loadGudangList();
// ================= BARANG KELUAR ================= account_biaya warehouse_id barang_id
$('.btn-out').click(function(){
// action='add';
resetFormKeluar();
$('#warehouse_id').closest('div').show();
$('#account_biaya').closest('div').show();
$('#modalKeluar').modal('show');
loadGudangList();
loadKasOut();
});
$('#warehouse_id_keluar').on('change', function(){
let warehouse_id = $(this).val();
// $('#wrap_item_select').addClass('d-none');
$('#barang_id').html('<option value="">Loading...</option>');
loadItems(warehouse_id);
});
// ================= ITEMS =================
function loadItems(warehouse_id){
return $.get("<?= base_url('items/get_items_by_wh_id/'); ?>" + warehouse_id, function(res){
if(res && res.length){
let opt = '<option value="">-- PILIH BARANG --</option>';
res.forEach(i=>{
opt += `<option value="${i.id}">
${i.kode_detail} - ${i.nama_barang} (Stok: ${i.stok})
</option>`;
});
$('#barang_id').html(opt);
$('#group_barang_keluar').removeClass('d-none');
$('#group_qty_keluar').removeClass('d-none');
$('#group_keterangan').removeClass('d-none');
} else {
$('#barang_id').html(opt);
}
}, 'json');
}
// ================= SAVE =================
$('#btnKeluar').click(function(){
let btn = $(this);
let data = {
account_biaya: $('#account_biaya').val(),
tanggal_keluar: $('#tanggal_keluar').val(),
warehouse_id_keluar: $('#warehouse_id_keluar').val(),
barang_id: $('#barang_id').val(),
qty_keluar: $('#qty_keluar').val(),
keterangan_keluar: $('#keterangan_keluar').val()
};
let url = "<?= base_url('items/keluarkan'); ?>";
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post(url, data, function(res){
if(res.status){
$('#modalItem').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil disimpan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Terjadi kesalahan'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Keluarkan');
});
});
// =====================================================
// ================= END URUSAN KELUAR ===============
// =====================================================
// ================= PRINT BARCODE =================
$(document).on('click', '.btn-print', function () {
@@ -787,78 +807,75 @@ $(document).on('click','.btn-detail',function(){
});
// ================= OPEN ADJUST =================
$(document).on('click','.btn-adjust',function(){
function rupiahToNumber(value){
let id = $(this).data('id');
if(!value) return 0;
if(!id){
alert('Pilih item dari tabel (gunakan tombol Adjust di baris)');
return;
}
return parseInt(
value.toString()
.replace(/[^\d]/g,'')
) || 0;
selectedItemId = id;
$('#adjust_item_id').val(id);
$('#adjust_qty').val('');
$('#adjust_ket').val('');
}
$('#modalAdjust').modal('show');
});
function hitungTotal(){
// ================= SAVE ADJUST =================
$('#btnAdjust').click(function(){
// ==========================
// HPP BARANG
// ==========================
let btn = $(this);
let qty = parseFloat($('#qty').val()) || 0;
let hargaBeli = rupiahToNumber($('#harga_beli').val());
let data = {
item_id: $('#adjust_item_id').val(),
qty: $('#adjust_qty').val(),
warehouse_id: $('#adjust_warehouse').val(),
keterangan: $('#adjust_ket').val()
};
let totalHpp = qty * hargaBeli;
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post("<?= base_url('items/adjust'); ?>", data, function(res){
// ==========================
// ACCOUNTING
// ==========================
if(res.status){
let totalAccount = 0;
$('#modalAdjust').modal('hide');
table.ajax.reload(null,false);
$('.nominal').each(function(){
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Stok berhasil disesuaikan',
timer: 1500,
showConfirmButton: false
});
totalAccount += rupiahToNumber($(this).val());
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Gagal melakukan penyesuaian'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Simpan');
});
});
// ==========================
// BALANCE
// ==========================
let balance = totalAccount - totalHpp;
$('#totalHppBarang').text(formatRupiah(totalHpp.toString()));
$('#totalAccount').text(formatRupiah(totalAccount.toString()));
if(balance == 0){
$('#totalAll')
.removeClass('text-danger')
.addClass('text-success')
.text('Balance');
$('#btnSimpan').prop('disabled',false);
}else{
let prefix = balance > 0 ? '+' : '-';
$('#totalAll')
.removeClass('text-success')
.addClass('text-danger')
.text(prefix + ' ' + formatRupiah(Math.abs(balance).toString()));
$('#btnSimpan').prop('disabled',true);
}
}
});
</script>
+406
View File
@@ -0,0 +1,406 @@
<style>
body {
background: #f5f7fb;
}
/* ===========================
CARD
=========================== */
.main-card,
.scanner-card,
.table-card {
border: none;
border-radius: 18px;
overflow: hidden;
/*box-shadow: 0 8px 25px rgba(0, 0, 0, .08);*/
}
/* ===========================
HEADER
=========================== */
.header-gradient {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: #fff;
padding: 22px 28px;
}
.info-box {
background: rgba(255,255,255,.18);
border-radius: 12px;
padding: 12px 16px;
}
.progress {
height: 8px;
border-radius: 20px;
}
/* ===========================
SCANNER
=========================== */
.barcode-avatar {
width: 110px;
height: 110px;
margin: auto;
border-radius: 50%;
background: #fff7e6;
color: #f59e0b;
display: flex;
justify-content: center;
align-items: center;
font-size: 45px;
}
.barcode-code {
font-size: 22px;
font-weight: 700;
letter-spacing: 2px;
}
.form-control-lg {
height: 60px;
font-size: 22px;
border-radius: 12px;
}
.form-control:focus {
border-color: #f59e0b;
box-shadow: 0 0 0 .2rem rgba(245, 158, 11, .2);
}
.scan-note {
color: #6c757d;
font-size: 13px;
}
.camera-btn {
height: 55px;
border-radius: 12px;
font-size: 17px;
}
/* ===========================
TABLE
=========================== */
.table thead th {
background: #d97706;
color: #fff;
border: none;
vertical-align: middle;
}
.table tbody td {
vertical-align: middle;
}
.table tbody tr:hover {
background: #fffaf0;
}
/* ===========================
BADGE
=========================== */
.badge-success-soft {
display: inline-block;
padding: 6px 14px;
border-radius: 30px;
background: #d1fae5;
color: #065f46;
font-weight: 600;
}
.badge-danger-soft {
display: inline-block;
padding: 6px 14px;
border-radius: 30px;
background: #fee2e2;
color: #991b1b;
font-weight: 600;
}
.badge-warning-soft {
display: inline-block;
padding: 6px 14px;
border-radius: 30px;
background: #fef3c7;
color: #92400e;
font-weight: 600;
}
#reader{
width:100%;
min-height:280px;
border-radius:12px;
overflow:hidden;
}
</style>
<div class="container mt-4">
<div class="card main-card">
<!-- HEADER -->
<div class="header-gradient">
<div class="row align-items-center">
<div class="col-md-8">
<h3 class="mb-1">
<i class="fa fa-barcode"></i>
Riwayat Perpindahan Barang
</h3>
<div class="small">
Silahkan scan / input QR id Barang
</div>
</div>
<div class="col-md-4 text-right">
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body">
<div class="row">
<!-- LEFT -->
<div class="col-lg-4 mb-4">
<div class="card scanner-card">
<div class="card-body">
<div class="barcode-avatar">
<i class="bi bi-qr-code-scan"></i>
</div>
<div class="text-center mt-3">
<div class="scan-note mt-2">
Scan QR ID
</div>
</div>
<hr>
<center>
<div id="scannerArea">
<input
type="text"
id="serial_number"
class="form-control form-control-lg text-center"
placeholder="Scan / Input QR ID"
autocomplete="off"
autofocus>
<small class="scan-note">
Scanner akan otomatis memanggil riwayat.
</small>
<button
type="button"
class="btn btn-warning btn-block camera-btn mt-4"
id="btnCamera">
<i class="fa fa-camera mr-2"></i>
Scan dengan Kamera
</button>
</div>
<div id="cameraArea" style="display:none;">
<div id="reader"></div>
<small class="scan-note d-block mt-2">
Arahkan QR Code ke kamera.
</small>
<button
type="button"
class="btn btn-secondary btn-block mt-3"
id="btnCloseCamera">
Tutup Kamera
</button>
</div>
</center>
<input type="hidden" id="barcode_id">
</div>
</div>
</div>
<!-- RIGHT -->
<div class="col-lg-8">
<div class="card table-card">
<div class="card-header bg-white">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
Riwayat Perpindahan Barang
</h5>
</div>
</div>
<div class="card-body border-bottom" id="itemInfo">
Belum ada data
</div>
<div class="table-responsive">
<table class="table table-hover mb-0" id="tableBarcode">
<thead>
<tr>
<th width="60">No</th>
<th>Date</th>
<th>Type</th>
<th>From</th>
<th>To</th>
<th>NOtes</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$("#serial_number").keypress(function(e){
if(e.which==13){
searchHistory();
}
});
function searchHistory(){
let barcode=$("#serial_number").val().trim();
if(barcode=="") return;
$.ajax({
url: `<?= base_url("items/scan_history"); ?>`,
type:"POST",
dataType:"json",
data:{
barcode:barcode
},
beforeSend:function(){
$("#serial_number").prop("disabled",true);
},
success:function(r){
$("#serial_number")
.prop("disabled",false)
.val("")
.focus();
if(!r.status){
Swal.fire({
icon:"error",
text:r.message
});
return;
}
renderItem(r.item);
renderHistory(r.history);
}
});
}
function renderItem(item){
let html='';
html+='<div class="row">';
html+=' <div class="col-md-6">';
html+=' <h4 class="mb-3"><b>'+item.nama_barang+'</b></h4>';
html+=' <div class="row mb-2">';
html+=' <div class="col-4 font-weight-bold">Kode</div>';
html+=' <div class="col-1">:</div>';
html+=' <div class="col-7">'+item.kode_detail+'</div>';
html+=' </div>';
html+=' <div class="row mb-2">';
html+=' <div class="col-4 font-weight-bold">Barcode</div>';
html+=' <div class="col-1">:</div>';
html+=' <div class="col-7">'+item.barcode+'</div>';
html+=' </div>';
html+=' </div>';
html+=' <div class="col-md-6">';
html+=' <div class="row mb-2">';
html+=' <div class="col-4 font-weight-bold">Serial Number</div>';
html+=' <div class="col-1">:</div>';
html+=' <div class="col-7">'+(item.serial_number ? item.serial_number : '-')+'</div>';
html+=' </div>';
html+=' <div class="row mb-2">';
html+=' <div class="col-4 font-weight-bold">Status</div>';
html+=' <div class="col-1">:</div>';
html+=' <div class="col-7">'+item.status+'</div>';
html+=' </div>';
html+=' <div class="row mb-2">';
html+=' <div class="col-4 font-weight-bold">Qty</div>';
html+=' <div class="col-1">:</div>';
html+=' <div class="col-7">'+item.qty_sisa+' / '+item.qty_awal+'</div>';
html+=' </div>';
html+=' </div>';
html+='</div>';
$("#itemInfo").html(html);
}
function renderHistory(list){
let html='';
$.each(list,function(i,v){
html+="<tr>";
html+="<td>"+(i+1)+"</td>";
html+="<td>"+v.created_at+"</td>";
html+="<td>"+v.movement_type+"</td>";
html+="<td>"+(v.from_warehouse??v.from_type)+"</td>";
html+="<td>"+(v.to_warehouse??v.to_type)+"</td>";
html+="<td>"+v.notes+"</td>";
html+="</tr>";
});
$("#tableBarcode tbody").html(html);
}
let html5QrCode=null;
$("#btnCamera").click(function(){
$("#scannerArea").hide();
$("#cameraArea").show();
html5QrCode=new Html5Qrcode("reader");
html5QrCode.start(
{
facingMode:"environment"
},
{
fps:10,
qrbox:220
},
function(decodedText){
html5QrCode.stop().then(function(){
$("#cameraArea").hide();
$("#scannerArea").show();
$("#serial_number")
.val(decodedText);
saveSerial();
});
}
);
});
$("#btnCloseCamera").click(function(){
if(html5QrCode){
html5QrCode.stop().then(function(){
$("#cameraArea").hide();
$("#scannerArea").show();
$("#serial_number").focus();
});
}
});
</script>
+306 -123
View File
@@ -108,12 +108,12 @@
type="text"
id="serial_number"
class="form-control form-control-lg text-center"
placeholder="Scan QR Barang"
placeholder="Scan Barcode / Serial Number / Nama Barang"
autocomplete="off"
autofocus>
<small class="scan-note">
Scanner akan otomatis menyimpan.
Scan atau ketik Barcode / Serial Number / Nama Barang, lalu Enter.
</small><br>
<button
@@ -147,7 +147,9 @@
</div>
</center>
<div id="tableBarcode"></div>
<div style="max-height: 200px; overflow: auto;">
<div class="mt-2" id="tableBarcode"></div>
</div>
<hr>
<div id="group_account_biaya">
@@ -246,29 +248,53 @@
// URUSAN BARCODE INPUT DAN CAMSCANNER
let daftarBarang = [];
let isSaving = false;
let searchTimer = null;
$(document).ready(function () {
$("#serial_number").focus();
$("#serial_number").keypress(function(e){
$("#serial_number").on("keypress", function(e){
if(e.which==13){
e.preventDefault();
if(searchTimer){
clearTimeout(searchTimer);
}
if(!isSaving){
getBarcodeId();
getBarcodeId(true);
}
}
});
$("#serial_number").on("input", function(){
let keyword = ($(this).val() || "").trim();
if(searchTimer){
clearTimeout(searchTimer);
}
if(keyword === ""){
daftarBarang = [];
renderTable();
return;
}
searchTimer = setTimeout(function(){
if(!isSaving){
getBarcodeId(false);
}
}, 300);
});
});
function getBarcodeId(){
function getBarcodeId(isScanner = false){
let barcode = ($("#serial_number").val() || "").trim();
if(barcode==""){
return;
}
$.post(
"<?=base_url('items/get_barcode_item');?>",
{
barcode:barcode
},
{ barcode: barcode },
function(r){
if(!r.status){
Swal.fire({
@@ -279,99 +305,174 @@ function getBarcodeId(){
$("#serial_number").val("").focus();
return;
}
let exist = daftarBarang.find(function(v){
return v.barcode_id == r.data.barcode_id;
});
if(exist){
let rows = Array.isArray(r.data) ? r.data : [];
if(rows.length === 0){
Swal.fire({
icon: "warning",
title: "Barcode sudah discan",
timer: 1000,
showConfirmButton: false
icon:"warning",
title:"Data kosong",
text:"Tidak ada barang yang cocok."
});
$("#serial_number").val("").focus();
return;
}
daftarBarang.push(r.data);
daftarBarang = rows;
renderTable();
$("#serial_number").val("").focus();
if(isScanner){
$("#serial_number").val("").focus();
}
},
"json"
);
}
function renderTable(){
function renderTable() {
let html='';
let html = '';
html+='<div class="table-responsive">';
html+='<table class="table table-bordered table-hover table-sm mb-0">';
html+='<thead>';
html+='<tr>';
html+='<th width="50">No</th>';
html+='<th width="180">Barcode</th>';
html+='<th width="150">Kode Barang</th>';
html+='<th>Nama Barang</th>';
html+='<th width="180">Serial Number</th>';
html+='<th width="80" class="text-center">Action</th>';
html+='</tr>';
html+='</thead>';
html+='<tbody>';
html += '<div class="table-responsive">';
html += '<table class="table table-bordered table-hover table-sm mb-0">';
html += '<thead>';
html += '<tr>';
html += '<th width="50">No</th>';
html += '<th width="180">Barcode</th>';
html += '<th width="120">Kode Barang</th>';
html += '<th>Nama Barang</th>';
html += '<th width="180">Serial Number</th>';
html += '<th width="70" class="text-center">Pilih</th>';
html += '</tr>';
html += '</thead>';
html += '<tbody>';
if(daftarBarang.length==0){
if (daftarBarang.length == 0) {
html+='<tr>';
html+='<td colspan="6" class="text-center text-muted py-4">';
html+='Belum ada barcode yang discan.';
html+='</td>';
html+='</tr>';
html += `
<tr>
<td colspan="6" class="text-center text-muted py-4">
Belum ada barang yang discan.
</td>
</tr>
`;
}else{
} else {
$.each(daftarBarang,function(i,v){
$.each(daftarBarang, function (i, v) {
html+='<tr>';
html+='<td>'+(i+1)+'</td>';
html+='<td><strong>'+v.barcode+'</strong></td>';
html+='<td>'+v.kode_detail+'</td>';
html+='<td>'+v.nama_barang+'</td>';
html+='<td>'+(v.serial_number?v.serial_number:'-')+'</td>';
html+='<td class="text-center">';
html+='<button class="btn btn-sm btn-outline-danger btnRemove" data-id="'+v.barcode_id+'">';
html+='<i class="bi bi-trash"></i>';
html+='</button>';
html+='</td>';
html+='</tr>';
html += `
<tr>
<td>${i + 1}</td>
<td><strong>${v.barcode}</strong></td>
<td>${v.kode_detail}</td>
<td>${v.nama_barang}</td>
<td>${v.serial_number ? v.serial_number : '-'}</td>
<td class="text-center align-middle">
<div class="form-check d-flex justify-content-center mb-0">
<input
class="form-check-input pilihBarang"
type="checkbox"
value="${v.barcode_id}"
data-barcode="${v.barcode}">
</div>
</td>
</tr>
`;
});
}
html+='</tbody>';
html+='</table>';
html+='</div>';
html += '</tbody>';
html += '</table>';
html += '</div>';
$("#tableBarcode").html(html);
$('#tableBarcode').html(html);
if (daftarBarang.length === 1) {
$(".pilihBarang").prop("checked", false);
$(".pilihBarang").first().prop("checked", true);
}
}
$(document).on("click",".btnRemove",function(){
// function renderTable(){
let id=$(this).data("id");
// let html='';
daftarBarang=daftarBarang.filter(function(v){
return v.barcode_id!=id;
});
// html+='<div class="table-responsive">';
// html+='<table class="table table-bordered table-hover table-sm mb-0">';
// html+='<thead>';
// html+='<tr>';
// html+='<th width="50">No</th>';
// html+='<th width="180">Barcode</th>';
// html+='<th width="150">Kode Barang</th>';
// html+='<th>Nama Barang</th>';
// html+='<th width="180">Serial Number</th>';
// html+='<th width="80" class="text-center">Action</th>';
// html+='</tr>';
// html+='</thead>';
// html+='<tbody>';
renderTable();
// if(daftarBarang.length==0){
$("#serial_number").focus();
// html+='<tr>';
// html+='<td colspan="6" class="text-center text-muted py-4">';
// html+='Belum ada barcode yang discan.';
// html+='</td>';
// html+='</tr>';
// }else{
// $.each(daftarBarang,function(i,v){
// html+='<tr>';
// html+='<td>'+(i+1)+'</td>';
// html+='<td><strong>'+v.barcode+'</strong></td>';
// html+='<td>'+v.kode_detail+'</td>';
// html+='<td>'+v.nama_barang+'</td>';
// html+='<td>'+(v.serial_number?v.serial_number:'-')+'</td>';
// html+='<td class="text-center">';
// html+='<button class="btn btn-sm btn-outline-danger btnRemove" data-id="'+v.barcode_id+'">';
// html+='<i class="bi bi-trash"></i>';
// html+='</button>';
// html+='</td>';
// html+='</tr>';
// });
// }
// html+='</tbody>';
// html+='</table>';
// html+='</div>';
// $("#tableBarcode").html(html);
// }
$(document).on("change", ".pilihBarang", function(){
if($(this).is(":checked")){
$(".pilihBarang").not(this).prop("checked", false);
}
});
function getSelectedBarcodeRows(){
let selectedIds = $(".pilihBarang:checked").map(function(){
return String($(this).val());
}).get();
if(selectedIds.length === 0){
return [];
}
return daftarBarang.filter(function(v){
return selectedIds.indexOf(String(v.barcode_id)) !== -1;
});
}
let html5QrCode=null;
@@ -400,7 +501,7 @@ $("#btnCamera").click(function(){
$("#serial_number")
.val(decodedText);
saveSerial();
getBarcodeId();
});
@@ -625,8 +726,9 @@ $('#btnSimpan').click(function(){
// ================= RESET FORM KELUAR =================
function resetFormKeluar(){
$('#tanggal_keluar').val('');
$('#qty_keluar').val('');
$('#keterangan_keluar').val('');
daftarBarang = [];
renderTable();
}
loadKasOut();
@@ -683,28 +785,41 @@ function loadItems(warehouse_id){
$('#btnKeluar').click(function(){
let btn = $(this);
let selectedRows = getSelectedBarcodeRows();
if(selectedRows.length !== 1){
Swal.fire({
icon: 'warning',
title: 'Pilih 1 barang',
text: 'Pengeluaran hanya boleh 1 barcode item per proses.'
});
return;
}
let selected = selectedRows[0];
let data = {
account_biaya: $('#account_biaya').val(),
tanggal_keluar: $('#tanggal_keluar').val(),
warehouse_id_keluar: $('#warehouse_id_keluar').val(),
barang_id: $('#barang_id').val(),
qty_keluar: $('#qty_keluar').val(),
keterangan_keluar: $('#keterangan_keluar').val()
account_biaya: $('#account_biaya').val(),
tanggal_keluar: $('#tanggal_keluar').val(),
keterangan_keluar: $('#keterangan_keluar').val(),
selected_barcode: selected.barcode_id
};
let url = "<?= base_url('items/keluarkan'); ?>";
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post(url, data, function(res){
if(res.status){
$('#modalItem').modal('hide');
$('#modalKeluar').modal('hide');
table.ajax.reload(null,false);
daftarBarang = [];
renderTable();
$("#serial_number").val("");
Swal.fire({
icon: 'success',
title: 'Berhasil',
@@ -741,68 +856,68 @@ $('#btnKeluar').click(function(){
// ================= END URUSAN KELUAR ===============
// =====================================================
// ================= DELETE =================
$(document).on('click','.btn-delete',function(){
// // ================= DELETE =================
// $(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
let btn = $(this);
// let id = $(this).data('id');
// let btn = $(this);
Swal.fire({
title: 'Hapus data?',
text: "Data tidak bisa dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
// Swal.fire({
// title: 'Hapus data?',
// text: "Data tidak bisa dikembalikan!",
// icon: 'warning',
// showCancelButton: true,
// confirmButtonColor: '#d33',
// cancelButtonColor: '#3085d6',
// confirmButtonText: 'Ya, hapus!',
// cancelButtonText: 'Batal'
// }).then((result) => {
if(result.isConfirmed){
// if(result.isConfirmed){
btn.prop('disabled', true).html('Menghapus...');
// btn.prop('disabled', true).html('Menghapus...');
$.get("<?= base_url('items/delete/'); ?>"+id,function(res){
// $.get("<?= base_url('items/delete/'); ?>"+id,function(res){
if(res.status){
// if(res.status){
table.ajax.reload(null,false);
// table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil dihapus',
timer: 1500,
showConfirmButton: false
});
// Swal.fire({
// icon: 'success',
// title: 'Berhasil',
// text: res.message || 'Data berhasil dihapus',
// timer: 1500,
// showConfirmButton: false
// });
}else{
// }else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Gagal menghapus data'
});
}
// Swal.fire({
// icon: 'error',
// title: 'Gagal',
// text: res.message || 'Gagal menghapus data'
// });
// }
},'json')
.fail(function(){
// },'json')
// .fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
// Swal.fire({
// icon: 'error',
// title: 'Error',
// text: 'Koneksi ke server gagal'
// });
})
.always(function(){
btn.prop('disabled', false).html('Hapus');
});
// })
// .always(function(){
// btn.prop('disabled', false).html('Hapus');
// });
}
});
// }
// });
});
// });
// ================= DETAIL =================
$(document).on('click','.btn-detail',function(){
@@ -920,5 +1035,73 @@ $('#btnAdjust').click(function(){
});
// ================= PRINT BARCODE =================
$(document).on('click', '.btn-print', function () {
let id = $(this).data('id');
Swal.fire({
title: 'Cetak Barcode',
html: `
<div class="text-left">
<div class="form-group">
<label for="print_column" class="font-weight-bold">
Jumlah Kolom
</label>
<select id="print_column" class="form-control">
<option value="1">1 Kolom</option>
<option value="2">2 Kolom</option>
<option value="3" selected>3 Kolom (Rekomendasi)</option>
</select>
</div>
<div class="form-group mb-0">
<label for="duplicat" class="font-weight-bold">
Jumlah Cetak per Barcode
</label>
<select id="duplicat" class="form-control">
<option value="1" selected>1 Label</option>
<option value="2">2 Label</option>
</select>
</div>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Cetak',
cancelButtonText: 'Batal',
confirmButtonColor: '#f0ad4e',
preConfirm: () => {
let column = $('#print_column').val();
let duplicat = $('#duplicat').val();
return {
column: column,
duplicat: duplicat
};
}
}).then((result) => {
if (!result.isConfirmed) {
return;
}
window.open(
"<?= base_url('items/print_barcode/'); ?>" +
id +
"?column=" + result.value.column +
"&duplicat=" + result.value.duplicat,
"_blank"
);
});
});
});
</script>
+40 -4
View File
@@ -16,6 +16,9 @@
<th>Kode</th>
<th>Nama</th>
<th>Limit Stok</th>
<th>Tracking Type</th>
<th>Need Serial</th>
<th>Unit</th>
<th>Aksi</th>
</tr>
</thead>
@@ -49,6 +52,25 @@
<label>Limit Stok</label>
<input type="number" id="limit_stock" class="form-control">
<label class="mt-2">Tracking Type</label>
<select id="tracking_type" class="form-control">
<option value="UNIT">UNIT</option>
<option value="QTY">QTY</option>
</select>
<label class="mt-2">Need Serial Number</label>
<select id="need_serial_number" class="form-control">
<option value="false">Tidak</option>
<option value="true">Ya</option>
</select>
<label class="mt-2">Unit</label>
<select id="unit" class="form-control">
<option value="PCS">PCS</option>
<option value="Meter">Meter</option>
<option value="Lembar">Lembar</option>
</select>
</div>
<div class="modal-footer">
@@ -77,6 +99,9 @@ $(function(){
$('#kode').val('');
$('#nama').val('');
$('#limit_stock').val('');
$('#tracking_type').val('UNIT');
$('#need_serial_number').val('false');
$('#unit').val('PCS');
}
// ADD
@@ -96,6 +121,9 @@ $(function(){
$('#kode').val(res.kode_barang);
$('#nama').val(res.nama);
$('#limit_stock').val(res.limit_stock);
$('#tracking_type').val((res.tracking_type || 'UNIT').toUpperCase());
$('#need_serial_number').val((res.need_serial_number || 'false').toLowerCase());
$('#unit').val(res.unit || 'PCS');
action = 'edit';
$('#modalKodebarang').modal('show');
@@ -110,11 +138,19 @@ $(function(){
id: $('#id').val(),
kode: $('#kode').val(),
nama: $('#nama').val(),
limit_stock: $('#limit_stock').val()
limit_stock: $('#limit_stock').val(),
tracking_type: $('#tracking_type').val(),
need_serial_number: $('#need_serial_number').val(),
unit: $('#unit').val()
};
if(!data.kode || !data.nama || !data.limit_stock){
Swal.fire('Warning','Nama gudang wajib diisi','warning');
if(!data.kode || !data.nama){
Swal.fire('Warning','Kode dan nama barang wajib diisi','warning');
return;
}
if(data.limit_stock === '' || parseInt(data.limit_stock) < 0){
Swal.fire('Warning','Limit stok wajib diisi dan tidak boleh negatif','warning');
return;
}
@@ -141,7 +177,7 @@ $(function(){
let id = $(this).data('id');
Swal.fire({
title:'Hapus gudang?',
title:'Hapus kode barang?',
text:'Pastikan tidak dipakai item',
icon:'warning',
showCancelButton:true
+80 -39
View File
@@ -3,23 +3,48 @@
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0 fw-bold">Neraca Saldo</h4>
<button type="button" class="btn btn-danger w-30" id="generate_pdf">
<h4 class="mb-0 fw-bold">Laporan Keuangan</h4>
<button type="button" class="btn btn-danger" id="generate_pdf">
Generate PDF
</button>
</div>
<div class="row mb-3">
<div class="col-md-3">
<label class="form-label">Tanggal Awal</label>
<input type="date" id="tanggal_awal" class="form-control"
value="<?= date('Y-m-01'); ?>">
</div>
<div class="col-md-3">
<label class="form-label">Tanggal Akhir</label>
<input type="date" id="tanggal_akhir" class="form-control"
value="<?= date('Y-m-t'); ?>">
</div>
<div class="col-md-2 d-flex align-items-end">
<button class="btn btn-primary w-100" id="btnFilter">
Tampilkan
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm table-striped table-hover align-middle">
<thead class="table-light">
<tr>
<th style="width: 120px;">Kode</th>
<th width="120">Kode</th>
<th>Akun</th>
<th class="text-end">Debit</th>
<th class="text-end">Kredit</th>
</tr>
</thead>
<tbody id="tb"></tbody>
<tfoot class="table-light">
<tr class="fw-bold">
<th colspan="2" class="text-end">Total</th>
@@ -27,6 +52,7 @@
<th class="text-end text-danger" id="tk"></th>
</tr>
</tfoot>
</table>
</div>
@@ -35,56 +61,71 @@
</div>
<script>
$(function(){
$(function () {
$.get("<?= base_url('neracasaldo/get_data'); ?>", function(res){
loadData();
let html = '';
let td = 0, tk = 0;
$('#btnFilter').click(function () {
loadData();
});
res.forEach(r=>{
function loadData() {
let debit = parseFloat(r.saldo_debit) || 0;
let kredit = parseFloat(r.saldo_kredit) || 0;
$.get("<?= base_url('neracasaldo/get_data'); ?>", {
td += debit;
tk += kredit;
tanggal_awal: $('#tanggal_awal').val(),
tanggal_akhir: $('#tanggal_akhir').val()
html += `
<tr>
<td class="fw-semibold text-muted">${r.kode_akun}</td>
<td>${r.nama_akun}</td>
<td class="text-end">${debit.toLocaleString()}</td>
<td class="text-end">${kredit.toLocaleString()}</td>
</tr>`;
});
}, function (res) {
$('#tb').html(html);
$('#td').text(td.toLocaleString());
$('#tk').text(tk.toLocaleString());
let html = '';
let td = 0;
let tk = 0;
}, 'json');
res.forEach(r => {
let debit = parseFloat(r.debit) || 0;
let kredit = parseFloat(r.kredit) || 0;
//GENERATE PDF
$('#generate_pdf').on('click', function(){
td += debit;
tk += kredit;
let btn = $(this);
html += `
<tr>
<td class="fw-semibold text-muted">${r.kode_akun}</td>
<td>${r.nama_akun}</td>
<td class="text-end">${debit.toLocaleString()}</td>
<td class="text-end">${kredit.toLocaleString()}</td>
</tr>
`;
});
btn.prop('disabled', true)
.html('Generate PDF...');
$('#tb').html(html);
$('#td').text(td.toLocaleString());
$('#tk').text(tk.toLocaleString());
window.open(
"<?= base_url('generateneracasaldo/generatepdf'); ?>",
'_blank'
);
}, 'json');
setTimeout(function(){
btn.prop('disabled', false)
.html('Generate PDF');
},1000);
}
$('#generate_pdf').click(function () {
let btn = $(this);
btn.prop('disabled', true).text('Generate PDF...');
window.open(
"<?= base_url('generateneracasaldo/generatepdf'); ?>" +
"?tanggal_awal=" + $('#tanggal_awal').val() +
"&tanggal_akhir=" + $('#tanggal_akhir').val(),
"_blank"
);
setTimeout(function () {
btn.prop('disabled', false).text('Generate PDF');
}, 1000);
});
});
});
</script>
+9 -3
View File
@@ -84,15 +84,15 @@
<i class="bi bi-graph-up"></i> Laporan
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item <?= ($active_menu == 'neraca_saldo') ? 'active' : '' ?>" href="<?= base_url('neracasaldo'); ?>">
<i class="bi bi-clipboard-data me-2"></i> Neraca Saldo</a>
</li>
<li><a class="dropdown-item <?= ($active_menu == 'laba_rugi') ? 'active' : '' ?>" href="<?= base_url('labarugi'); ?>">
<i class="bi bi-graph-up-arrow me-2"></i> Laba Rugi</a>
</li>
<li><a class="dropdown-item <?= ($active_menu == 'neraca') ? 'active' : '' ?>" href="<?= base_url('neraca'); ?>">
<i class="bi bi-bar-chart-line me-2"></i> Neraca</a>
</li>
<li><a class="dropdown-item <?= ($active_menu == 'neraca_saldo') ? 'active' : '' ?>" href="<?= base_url('neracasaldo'); ?>">
<i class="bi bi-clipboard-data me-2"></i> Laporan Harian</a>
</li>
</ul>
</li>
@@ -180,6 +180,12 @@
<i class="bi bi-box me-2"></i> Daftar Barang
</a>
</li><hr class="dropdown-divider">
<li>
<a class="dropdown-item <?= ($active_menu == 'item_historys') ? 'active' : '' ?>"
href="<?= base_url('items/item_barcodes_history'); ?>">
<i class="bi bi-clock me-2"></i> History Barang
</a>
</li>
<li>
<a class="dropdown-item <?= ($active_menu == 'kode_barang') ? 'active' : '' ?>"
href="<?= base_url('kodebarang'); ?>">