diff --git a/config.php b/config.php index 27cfbc4e3..b0ada2a5b 100644 --- a/config.php +++ b/config.php @@ -102,7 +102,7 @@ function env(string $key, $default = null) ])); // Developer mode -define('DEV_MODE', env('DEVMODE') ?: 'true'); +define('DEV_MODE', true); define('JWT_SECRET', 'mlite_secret_key_change_me'); diff --git a/mlite_db.sql b/mlite_db.sql index e7f23dc10..6ec8f383b 100644 --- a/mlite_db.sql +++ b/mlite_db.sql @@ -1651,8 +1651,8 @@ CREATE TABLE `mlite_bpjs_emr_device` ( `kode_produk` VARCHAR(100) DEFAULT NULL, `keterangan` TEXT DEFAULT NULL, `manufacturer` VARCHAR(255) DEFAULT NULL, - 'manufacture_date' DATE NOT NULL, - 'expiration_date' DATE NOT NULL, + 'manufacture_date' DATE DEFAULT NULL, + 'expiration_date' DATE DEFAULT NULL, `model` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uq_device_id` (`device_id`), @@ -2060,7 +2060,7 @@ CREATE TABLE `mlite_esignatures` ( PRIMARY KEY (`id`), KEY `ref_idx` (`ref_type`,`ref_id`), KEY `hash_idx` (`signature_hash`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; CREATE TABLE `mlite_geolocation_presensi` ( @@ -2798,7 +2798,7 @@ CREATE TABLE `mlite_sertisign_webhook` ( PRIMARY KEY (`id`), KEY `transaction_idx` (`transaction_id`), KEY `status_idx` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; CREATE TABLE `mlite_mapping_snomed_icd` ( diff --git a/plugins/kasir_rawat_inap/Admin.php b/plugins/kasir_rawat_inap/Admin.php index e3db0565b..0ee90b0c8 100644 --- a/plugins/kasir_rawat_inap/Admin.php +++ b/plugins/kasir_rawat_inap/Admin.php @@ -1041,7 +1041,7 @@ public function anyRincian() // Prepare ingredient row (hide prices) $row['nomor'] = ''; - $row['nama_brng'] = "     - " . $row['nama_brng']; + $row['nama_brng'] = str_repeat("\u{00A0}", 4) . ' - ' . $row['nama_brng']; $row['total'] = 0; $row['embalase'] = 0; $row['tuslah'] = 0; diff --git a/plugins/kasir_rawat_jalan/Admin.php b/plugins/kasir_rawat_jalan/Admin.php index 6380e8a4d..1568ec36f 100644 --- a/plugins/kasir_rawat_jalan/Admin.php +++ b/plugins/kasir_rawat_jalan/Admin.php @@ -17,6 +17,7 @@ public function navigation() 'Kelola' => 'manage', 'Kasir' => 'shift', 'Laporan' => 'report', + 'Rekap Shift' => 'shiftreport', ]; } @@ -822,7 +823,7 @@ public function anyRincian() // Prepare ingredient row (hide prices) $row['nomor'] = ''; - $row['nama_brng'] = "     - " . $row['nama_brng']; + $row['nama_brng'] = str_repeat("\u{00A0}", 4) . ' - ' . $row['nama_brng']; $row['total'] = 0; $row['embalase'] = 0; $row['tuslah'] = 0; @@ -2006,7 +2007,7 @@ public function anyFaktur() if ($row) { $total_racikan += $row['total']; - $row['nama_brng'] = "     - " . $row['nama_brng']; + $row['nama_brng'] = str_repeat("\u{00A0}", 4) . ' - ' . $row['nama_brng']; $row['total'] = 0; $row['jml'] = 0; $row['biaya_obat'] = 0; @@ -2298,7 +2299,7 @@ public function anyReport() $detailStmt->execute([$awal, $akhir]); $details = $detailStmt->fetchAll(); - return $this->draw('report.html', ['awal' => $awal, 'akhir' => $akhir, 'rows' => $rows, 'details' => $details]); + return $this->draw('report.html', ['awal' => $awal, 'akhir' => $akhir, 'rows' => $rows, 'details' => $details, 'settings' => $this->settings('settings')]); } public function anyReportExport() @@ -2317,4 +2318,32 @@ public function anyReportExport() exit(); } + public function anyShiftReport() + { + $this->_addHeaderFiles(); + $awal = isset($_GET['awal']) ? $_GET['awal'] : date('Y-m-d').' 00:00:00'; + $akhir = isset($_GET['akhir']) ? $_GET['akhir'] : date('Y-m-d').' 23:59:59'; + $pdo = $this->db()->pdo(); + $stmt = $pdo->prepare("SELECT s.id_shift, s.user_id, COALESCE(p.nama, u.fullname) AS nama_kasir, s.waktu_buka, s.waktu_tutup, IFNULL(s.kas_awal,0) kas_awal, IFNULL(s.total_transaksi,0) total_transaksi, IFNULL(s.kas_akhir,0) kas_akhir, IFNULL(s.selisih,0) selisih, s.keterangan FROM mlite_kasir_shift s LEFT JOIN mlite_users u ON u.id=s.user_id LEFT JOIN pegawai p ON p.nik=u.username WHERE (s.waktu_buka BETWEEN ? AND ?) OR (s.waktu_tutup BETWEEN ? AND ?) OR (s.waktu_buka <= ? AND (s.waktu_tutup IS NULL OR s.waktu_tutup >= ?)) ORDER BY s.waktu_buka ASC, s.id_shift ASC"); + $stmt->execute([$awal, $akhir, $awal, $akhir, $awal, $akhir]); + $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); + return $this->draw('shift.report.html', ['awal' => $awal, 'akhir' => $akhir, 'rows' => $rows, 'settings' => $this->settings('settings')]); + } + + public function anyShiftReportExport() + { + $awal = isset($_GET['awal']) ? $_GET['awal'] : date('Y-m-d').' 00:00:00'; + $akhir = isset($_GET['akhir']) ? $_GET['akhir'] : date('Y-m-d').' 23:59:59'; + header('Content-Type: text/csv'); + header('Content-Disposition: attachment; filename="rekap_shift_kasir.csv"'); + $pdo = $this->db()->pdo(); + $stmt = $pdo->prepare("SELECT s.id_shift, s.user_id, COALESCE(p.nama, u.fullname) AS nama_kasir, s.waktu_buka, s.waktu_tutup, IFNULL(s.kas_awal,0) kas_awal, IFNULL(s.total_transaksi,0) total_transaksi, IFNULL(s.kas_akhir,0) kas_akhir, IFNULL(s.selisih,0) selisih, s.keterangan FROM mlite_kasir_shift s LEFT JOIN mlite_users u ON u.id=s.user_id LEFT JOIN pegawai p ON p.nik=u.username WHERE (s.waktu_buka BETWEEN ? AND ?) OR (s.waktu_tutup BETWEEN ? AND ?) OR (s.waktu_buka <= ? AND (s.waktu_tutup IS NULL OR s.waktu_tutup >= ?)) ORDER BY s.waktu_buka ASC, s.id_shift ASC"); + $stmt->execute([$awal, $akhir, $awal, $akhir, $awal, $akhir]); + echo "id_shift,user_id,nama_kasir,waktu_buka,waktu_tutup,kas_awal,total_transaksi,kas_akhir,selisih,keterangan\n"; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + echo ($row['id_shift'] ?? '').",".($row['user_id'] ?? '').",\"".str_replace('\"', '\"\"', (string)($row['nama_kasir'] ?? ''))."\",".($row['waktu_buka'] ?? '').",".($row['waktu_tutup'] ?? '').",".($row['kas_awal'] ?? 0).",".($row['total_transaksi'] ?? 0).",".($row['kas_akhir'] ?? 0).",".($row['selisih'] ?? 0).",\"".str_replace('\"', '\"\"', (string)($row['keterangan'] ?? ''))."\"\n"; + } + exit(); + } + } diff --git a/plugins/kasir_rawat_jalan/view/admin/report.html b/plugins/kasir_rawat_jalan/view/admin/report.html index 52e8f3800..5b2e7d7fe 100644 --- a/plugins/kasir_rawat_jalan/view/admin/report.html +++ b/plugins/kasir_rawat_jalan/view/admin/report.html @@ -26,6 +26,37 @@

Laporan Kasir


+
@@ -91,8 +122,9 @@

Laporan Kasir

function printContent(el){ var restore = document.body.innerHTML; + var kop = document.getElementById('kop_print') ? document.getElementById('kop_print').innerHTML : ''; var content = document.getElementById(el).innerHTML; - document.body.innerHTML = content; + document.body.innerHTML = kop + content; window.print(); document.body.innerHTML = restore; } diff --git a/plugins/kasir_rawat_jalan/view/admin/shift.report.html b/plugins/kasir_rawat_jalan/view/admin/shift.report.html new file mode 100644 index 000000000..d37b6b09b --- /dev/null +++ b/plugins/kasir_rawat_jalan/view/admin/shift.report.html @@ -0,0 +1,157 @@ +
+
+

Rekap Shift Kasir

+
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + Export CSV + +
+
+
+ +
+
+ + + + + + + + + + + + +
+ + +

+ {?=isset_or($settings.nama_instansi)?}
+ Alamat: {?=isset_or($settings.alamat)?} - {?=isset_or($settings.kota)?} - {?=isset_or($settings.propinsi)?}
+ Telepon: {?=isset_or($settings.nomor_telepon)?} - Email: {?=isset_or($settings.email)?} +

+
+
+
+
REKAP SHIFT KASIR RAWAT JALAN
+
Periode {?=isset_or($awal)?} s/d {?=isset_or($akhir)?}
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID ShiftID KasirNama KasirWaktu BukaWaktu TutupStatusKas AwalTotal TransaksiKas AkhirSelisihKeterangan
Total
+
+ + + + diff --git a/plugins/keuangan/Admin.php b/plugins/keuangan/Admin.php index dfb97bfef..0a25333ef 100644 --- a/plugins/keuangan/Admin.php +++ b/plugins/keuangan/Admin.php @@ -96,12 +96,16 @@ public function getRekeningTahun() $this->core->addJS(url([ADMIN, 'keuangan', 'akunrekeningjs']), 'footer'); $this->_addHeaderFiles(); $curr_year = date('Y'); + $year = isset($_GET['tahun']) ? (int) $_GET['tahun'] : (int) $curr_year; + if ($year < 2000 || $year > ((int) date('Y') + 5)) { + $year = (int) $curr_year; + } $akunrekening = $this->db('mlite_rekening')->toArray(); $rekeningtahun = $this->db('mlite_rekeningtahun') ->join('mlite_rekening', 'mlite_rekening.kd_rek=mlite_rekeningtahun.kd_rek') - ->where('thn', $curr_year) + ->where('thn', $year) ->toArray(); - return $this->draw('rekening.tahun.html', ['akunrekening' => $akunrekening, 'rekeningtahun' => $rekeningtahun]); + return $this->draw('rekening.tahun.html', ['akunrekening' => $akunrekening, 'rekeningtahun' => $rekeningtahun, 'tahun_filter' => $year]); } public function postSaveRekeningTahun() @@ -215,8 +219,10 @@ public function getBukuBesar() } $kd_rek = isset($_GET['kd_rek']) ? $_GET['kd_rek'] : ''; + $showSaldoAkhir = !empty($_GET['show_saldo_akhir']); + $showSaldoAkhirInt = $showSaldoAkhir ? 1 : 0; - $sql = "SELECT mlite_detailjurnal.no_jurnal, tgl_jurnal, keterangan, debet, kredit, mlite_rekening.balance FROM mlite_detailjurnal JOIN mlite_jurnal ON mlite_detailjurnal.no_jurnal = mlite_jurnal.no_jurnal JOIN mlite_rekening ON mlite_detailjurnal.kd_rek = mlite_rekening.kd_rek WHERE (mlite_jurnal.tgl_jurnal BETWEEN ? AND ?)"; + $sql = "SELECT mlite_detailjurnal.no_jurnal, tgl_jurnal, keterangan, debet, kredit, mlite_detailjurnal.kd_rek, mlite_rekening.nm_rek, mlite_rekening.balance FROM mlite_detailjurnal JOIN mlite_jurnal ON mlite_detailjurnal.no_jurnal = mlite_jurnal.no_jurnal JOIN mlite_rekening ON mlite_detailjurnal.kd_rek = mlite_rekening.kd_rek WHERE (mlite_jurnal.tgl_jurnal BETWEEN ? AND ?)"; $params = [$tgl_awal, $tgl_akhir]; if(!empty($kd_rek)) { @@ -224,21 +230,256 @@ public function getBukuBesar() $params[] = $kd_rek; } - $sql .= " ORDER BY mlite_detailjurnal.no_jurnal ASC"; + if (!empty($kd_rek)) { + $sql .= " ORDER BY mlite_jurnal.tgl_jurnal ASC, mlite_detailjurnal.no_jurnal ASC"; + } else { + $sql .= " ORDER BY mlite_detailjurnal.kd_rek ASC, mlite_jurnal.tgl_jurnal ASC, mlite_detailjurnal.no_jurnal ASC"; + } $query = $this->db()->pdo()->prepare($sql); $query->execute($params); - $bukubesar = $query->fetchAll(\PDO::FETCH_ASSOC); - $saldo = 0; - foreach ($bukubesar as $key => $row) { - $debet = (float)$row['debet']; - $kredit = (float)$row['kredit']; - if ($row['balance'] === 'D') { - $saldo += ($debet - $kredit); + $rows = $query->fetchAll(\PDO::FETCH_ASSOC); + $bukubesar = []; + $summary = [ + 'rekening_count' => 0, + 'total_debet' => 0, + 'total_kredit' => 0, + 'saldo_akhir_d' => 0, + 'saldo_akhir_k' => 0, + 'saldo_akhir_net' => 0, + 'saldo_akhir_side' => '', + 'saldo_akhir_amount' => 0, + 'saldo_akhir_net_side' => '', + 'saldo_akhir_net_amount' => 0, + ]; + + if (!empty($kd_rek)) { + $saldo = 0; + $tahun = (int) date('Y', strtotime($tgl_awal)); + $saldoAwalRow = $this->db('mlite_rekeningtahun')->where('thn', $tahun)->where('kd_rek', $kd_rek)->oneArray(); + $saldoAwal = (float) ($saldoAwalRow['saldo_awal'] ?? 0); + + $rekeningRow = $this->db('mlite_rekening')->where('kd_rek', $kd_rek)->oneArray(); + $nmRek = (string) ($rekeningRow['nm_rek'] ?? ''); + $balance = (string) ($rekeningRow['balance'] ?? 'D'); + + $awalTahun = $tahun . '-01-01'; + $sqlMutasi = " + SELECT COALESCE(SUM( + CASE + WHEN r.balance = 'D' THEN COALESCE(jd.debet, 0) - COALESCE(jd.kredit, 0) + ELSE COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0) + END + ), 0) AS mutasi + FROM mlite_detailjurnal jd + JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal + JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek + WHERE jd.kd_rek = ? + AND j.tgl_jurnal >= ? + AND j.tgl_jurnal < ? + "; + $stMutasi = $this->db()->pdo()->prepare($sqlMutasi); + $stMutasi->execute([$kd_rek, $awalTahun, $tgl_awal]); + $mutasi = (float) (($stMutasi->fetch()['mutasi'] ?? 0)); + + $saldo = $saldoAwal + $mutasi; + $saldoAbs = abs($saldo); + $saldoSisi = $saldo >= 0 ? $balance : ($balance === 'D' ? 'K' : 'D'); + $summary['rekening_count'] = 1; + $bukubesar[] = [ + 'no_jurnal' => '', + 'tgl_jurnal' => $tgl_awal, + 'kd_rek' => $kd_rek, + 'nm_rek' => $nmRek, + 'keterangan' => 'Saldo awal', + 'debet' => 0, + 'kredit' => 0, + 'balance' => $balance, + 'saldo' => $saldo, + 'saldo_normal' => $saldoAbs, + 'saldo_sisi' => $saldoSisi, + 'row_type' => 'saldo_awal', + ]; + foreach ($rows as $row) { + $debet = (float) ($row['debet'] ?? 0); + $kredit = (float) ($row['kredit'] ?? 0); + if (($row['balance'] ?? '') === 'D') { + $saldo += ($debet - $kredit); + } else { + $saldo += ($kredit - $debet); + } + $row['saldo'] = $saldo; + $rowBalance = (string) ($row['balance'] ?? $balance); + $row['saldo_normal'] = abs((float) $saldo); + $row['saldo_sisi'] = $saldo >= 0 ? $rowBalance : ($rowBalance === 'D' ? 'K' : 'D'); + $row['row_type'] = 'mutasi'; + $summary['total_debet'] += $debet; + $summary['total_kredit'] += $kredit; + $bukubesar[] = $row; + } + + $saldoAbs = abs((float) $saldo); + $saldoSisi = $saldo >= 0 ? $balance : ($balance === 'D' ? 'K' : 'D'); + if ($showSaldoAkhir) { + $bukubesar[] = [ + 'no_jurnal' => '', + 'tgl_jurnal' => $tgl_akhir, + 'kd_rek' => $kd_rek, + 'nm_rek' => $nmRek, + 'keterangan' => 'Saldo akhir', + 'debet' => 0, + 'kredit' => 0, + 'balance' => $balance, + 'saldo' => $saldo, + 'saldo_normal' => $saldoAbs, + 'saldo_sisi' => $saldoSisi, + 'row_type' => 'saldo_akhir', + ]; + } + + if ($saldoSisi === 'D') { + $summary['saldo_akhir_d'] = $saldoAbs; + $summary['saldo_akhir_net'] = $saldoAbs; } else { - $saldo += ($kredit - $debet); + $summary['saldo_akhir_k'] = $saldoAbs; + $summary['saldo_akhir_net'] = -$saldoAbs; + } + $summary['saldo_akhir_side'] = $saldoSisi; + $summary['saldo_akhir_amount'] = $saldoAbs; + $summary['saldo_akhir_net_side'] = $saldoSisi; + $summary['saldo_akhir_net_amount'] = $saldoAbs; + } else { + $tahun = (int) date('Y', strtotime($tgl_awal)); + $awalTahun = $tahun . '-01-01'; + + $saldoAwalRows = $this->db('mlite_rekeningtahun')->where('thn', $tahun)->toArray(); + $saldoAwalMap = []; + foreach ($saldoAwalRows as $r) { + $saldoAwalMap[(string) $r['kd_rek']] = (float) ($r['saldo_awal'] ?? 0); + } + + $sqlMutasiAll = " + SELECT jd.kd_rek, COALESCE(SUM( + CASE + WHEN r.balance = 'D' THEN COALESCE(jd.debet, 0) - COALESCE(jd.kredit, 0) + ELSE COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0) + END + ), 0) AS mutasi + FROM mlite_detailjurnal jd + JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal + JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek + WHERE j.tgl_jurnal >= ? + AND j.tgl_jurnal < ? + GROUP BY jd.kd_rek + "; + $stMutasiAll = $this->db()->pdo()->prepare($sqlMutasiAll); + $stMutasiAll->execute([$awalTahun, $tgl_awal]); + $mutasiMap = []; + foreach ($stMutasiAll->fetchAll(\PDO::FETCH_ASSOC) as $m) { + $mutasiMap[(string) $m['kd_rek']] = (float) ($m['mutasi'] ?? 0); + } + + $rowsByKd = []; + $kdSet = []; + foreach ($rows as $row) { + $k = (string) ($row['kd_rek'] ?? ''); + if ($k === '') { + continue; + } + $rowsByKd[$k][] = $row; + $kdSet[$k] = true; + } + foreach (array_keys($saldoAwalMap) as $k) { + $kdSet[$k] = true; } - $bukubesar[$key]['saldo'] = $saldo; + $kds = array_keys($kdSet); + sort($kds); + + $saldoPerKd = []; + foreach ($kds as $k) { + $saldoPerKd[$k] = (float) ($saldoAwalMap[$k] ?? 0) + (float) ($mutasiMap[$k] ?? 0); + } + + foreach ($kds as $k) { + $hasRows = !empty($rowsByKd[$k]); + $hasSaldoAwal = abs((float) ($saldoAwalMap[$k] ?? 0)) > 0.000001; + if (!$hasRows && !$hasSaldoAwal) { + continue; + } + + $rekeningRow = $this->db('mlite_rekening')->where('kd_rek', $k)->oneArray(); + $nmRek = (string) ($rekeningRow['nm_rek'] ?? ''); + $balance = (string) ($rekeningRow['balance'] ?? 'D'); + + $summary['rekening_count']++; + $saldoAbs = abs((float) $saldoPerKd[$k]); + $saldoSisi = $saldoPerKd[$k] >= 0 ? $balance : ($balance === 'D' ? 'K' : 'D'); + $bukubesar[] = [ + 'no_jurnal' => '', + 'tgl_jurnal' => $tgl_awal, + 'kd_rek' => $k, + 'nm_rek' => $nmRek, + 'keterangan' => 'Saldo awal', + 'debet' => 0, + 'kredit' => 0, + 'balance' => $balance, + 'saldo' => $saldoPerKd[$k], + 'saldo_normal' => $saldoAbs, + 'saldo_sisi' => $saldoSisi, + 'row_type' => 'saldo_awal', + ]; + + foreach (($rowsByKd[$k] ?? []) as $row) { + $debet = (float) ($row['debet'] ?? 0); + $kredit = (float) ($row['kredit'] ?? 0); + if (($row['balance'] ?? '') === 'D') { + $saldoPerKd[$k] += ($debet - $kredit); + } else { + $saldoPerKd[$k] += ($kredit - $debet); + } + $row['saldo'] = $saldoPerKd[$k]; + $rowBalance = (string) ($row['balance'] ?? $balance); + $row['saldo_normal'] = abs((float) $saldoPerKd[$k]); + $row['saldo_sisi'] = $saldoPerKd[$k] >= 0 ? $rowBalance : ($rowBalance === 'D' ? 'K' : 'D'); + $row['row_type'] = 'mutasi'; + $summary['total_debet'] += $debet; + $summary['total_kredit'] += $kredit; + $bukubesar[] = $row; + } + + $saldoAkhirAbs = abs((float) $saldoPerKd[$k]); + $saldoAkhirSisi = $saldoPerKd[$k] >= 0 ? $balance : ($balance === 'D' ? 'K' : 'D'); + if ($showSaldoAkhir) { + $bukubesar[] = [ + 'no_jurnal' => '', + 'tgl_jurnal' => $tgl_akhir, + 'kd_rek' => $k, + 'nm_rek' => $nmRek, + 'keterangan' => 'Saldo akhir', + 'debet' => 0, + 'kredit' => 0, + 'balance' => $balance, + 'saldo' => $saldoPerKd[$k], + 'saldo_normal' => $saldoAkhirAbs, + 'saldo_sisi' => $saldoAkhirSisi, + 'row_type' => 'saldo_akhir', + ]; + } + + if ($saldoAkhirSisi === 'D') { + $summary['saldo_akhir_d'] += $saldoAkhirAbs; + $summary['saldo_akhir_net'] += $saldoAkhirAbs; + } else { + $summary['saldo_akhir_k'] += $saldoAkhirAbs; + $summary['saldo_akhir_net'] -= $saldoAkhirAbs; + } + } + + $net = (float) $summary['saldo_akhir_net']; + $netAbs = abs($net); + $netSisi = $net >= 0 ? 'D' : 'K'; + $summary['saldo_akhir_net_side'] = $netSisi; + $summary['saldo_akhir_net_amount'] = $netAbs; } $akunrekening = $this->db('mlite_rekening')->toArray(); @@ -246,7 +487,10 @@ public function getBukuBesar() if(isset($_GET['action']) && $_GET['action'] == 'print') { echo $this->draw('buku.besar.print.html', [ 'bukubesar' => $bukubesar, - 'action' => url([ADMIN,'keuangan','bukubesar']) + 'action' => url([ADMIN,'keuangan','bukubesar']), + 'kd_rek_filter' => $kd_rek, + 'show_saldo_akhir' => $showSaldoAkhirInt, + 'summary' => $summary, ]); exit(); } else { @@ -254,8 +498,10 @@ public function getBukuBesar() 'bukubesar' => $bukubesar, 'akunrekening' => $akunrekening, 'kd_rek_filter' => $kd_rek, + 'show_saldo_akhir' => $showSaldoAkhirInt, 'tgl_awal' => htmlspecialchars($tgl_awal, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), 'tgl_akhir' => htmlspecialchars($tgl_akhir, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), + 'summary' => $summary, ]); } } @@ -287,23 +533,30 @@ public function getCashFlow() array("tipe" => "M", "arus_kas" => "Kegiatan Pendanaan"), ); + $cashAccounts = ['1101', '1102', '1103', '1104', '1105']; + $cashPlaceholders = implode(',', array_fill(0, count($cashAccounts), '?')); + // Hitung saldo awal kas dari akun kas (1101-1105) sebelum periode $query_saldo_awal = " SELECT COALESCE(SUM( CASE - WHEN r.balance = 'D' THEN COALESCE(jd.debet, 0) - COALESCE(jd.kredit, 0) - ELSE COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0) + WHEN j.tgl_jurnal < ? THEN + CASE + WHEN r.balance = 'D' THEN COALESCE(jd.debet, 0) - COALESCE(jd.kredit, 0) + ELSE COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0) + END + ELSE 0 END ), 0) as saldo_kas FROM mlite_rekening r LEFT JOIN mlite_detailjurnal jd ON r.kd_rek = jd.kd_rek - LEFT JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal AND j.tgl_jurnal < ? - WHERE r.kd_rek IN ('1101', '1102', '1103', '1104', '1105') + LEFT JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal + WHERE r.kd_rek IN ($cashPlaceholders) AND r.tipe = 'N' "; $stmt_saldo = $this->db()->pdo()->prepare($query_saldo_awal); - $stmt_saldo->execute([$tgl_awal]); + $stmt_saldo->execute(array_merge([$tgl_awal], $cashAccounts)); $result_saldo = $stmt_saldo->fetch(); $saldo_awal_kas = $result_saldo['saldo_kas'] ?? 0; @@ -311,85 +564,136 @@ public function getCashFlow() $total_debet = 0; $total_saldo_kredit = 0; $total_saldo_debet = 0; - $n = 1; + $sqlJurnal = " + SELECT + j.no_jurnal, + j.tgl_jurnal, + jd.kd_rek, + r.nm_rek, + r.tipe, + r.balance, + jd.debet, + jd.kredit + FROM mlite_jurnal j + JOIN mlite_detailjurnal jd ON jd.no_jurnal = j.no_jurnal + JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek + WHERE j.tgl_jurnal >= ? AND j.tgl_jurnal <= ? + AND j.no_jurnal IN ( + SELECT DISTINCT no_jurnal FROM mlite_detailjurnal WHERE kd_rek IN ($cashPlaceholders) + ) + ORDER BY j.tgl_jurnal ASC, j.no_jurnal ASC + "; + $stmtJurnal = $this->db()->pdo()->prepare($sqlJurnal); + $stmtJurnal->execute(array_merge([$tgl_awal, $tgl_akhir], $cashAccounts)); + $detail = $stmtJurnal->fetchAll(\PDO::FETCH_ASSOC); + + $byJurnal = []; + foreach ($detail as $r) { + $no = (string) ($r['no_jurnal'] ?? ''); + if ($no === '') { + continue; + } + $byJurnal[$no][] = $r; + } + + $agg = [ + 'R' => ['masuk' => [], 'keluar' => []], + 'N' => ['masuk' => [], 'keluar' => []], + 'M' => ['masuk' => [], 'keluar' => []], + ]; + + foreach ($byJurnal as $noJurnal => $lines) { + $cashDelta = 0.0; + foreach ($lines as $ln) { + $kd = (string) ($ln['kd_rek'] ?? ''); + if (in_array($kd, $cashAccounts, true)) { + $cashDelta += (float) ($ln['debet'] ?? 0) - (float) ($ln['kredit'] ?? 0); + } + } + if (abs($cashDelta) < 0.000001) { + continue; + } + + $isIn = $cashDelta > 0; + $weights = []; + $totalWeight = 0.0; + foreach ($lines as $ln) { + $kd = (string) ($ln['kd_rek'] ?? ''); + if ($kd === '' || in_array($kd, $cashAccounts, true)) { + continue; + } + $debet = (float) ($ln['debet'] ?? 0); + $kredit = (float) ($ln['kredit'] ?? 0); + $w = $isIn ? max($kredit - $debet, 0) : max($debet - $kredit, 0); + if ($w <= 0) { + continue; + } + $weights[] = [$ln, $w]; + $totalWeight += $w; + } + + if ($totalWeight <= 0) { + continue; + } + + foreach ($weights as $item) { + [$ln, $w] = $item; + $tipe = (string) ($ln['tipe'] ?? ''); + if (!isset($agg[$tipe])) { + continue; + } + $kd = (string) ($ln['kd_rek'] ?? ''); + $nm = (string) ($ln['nm_rek'] ?? ''); + $amount = abs($cashDelta) * ($w / $totalWeight); + + $bucket = $isIn ? 'masuk' : 'keluar'; + if (!isset($agg[$tipe][$bucket][$kd])) { + $agg[$tipe][$bucket][$kd] = [ + 'kd_rek' => $kd, + 'nm_rek' => $nm, + 'kredit_all' => 0, + 'debet_all' => 0, + 'saldo_awal' => 0, + ]; + } + + if ($isIn) { + $agg[$tipe][$bucket][$kd]['kredit_all'] += $amount; + } else { + $agg[$tipe][$bucket][$kd]['debet_all'] += $amount; + } + } + } + + $n = 1; foreach ($rows_aruskas as $row) { + $tipe = (string) ($row['tipe'] ?? ''); $row['nomor'] = $n++; $row['total_masuk'] = 0; $row['total_keluar'] = 0; $row['total_saldo_awal_masuk'] = 0; $row['total_saldo_awal_keluar'] = 0; - // Arus kas masuk (transaksi yang menambah kas) dalam periode - $query_masuk = " - SELECT - jd.kd_rek, - r.nm_rek, - r.tipe, - r.balance, - SUM(CASE - WHEN jd.kd_rek IN ('1101', '1102', '1103', '1104', '1105') THEN jd.debet - ELSE jd.kredit - END) as total_masuk - FROM mlite_detailjurnal jd - JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek - JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal - WHERE r.tipe = ? - AND j.tgl_jurnal >= ? AND j.tgl_jurnal <= ? - AND ((jd.kd_rek IN ('1101', '1102', '1103', '1104', '1105') AND jd.debet > 0) - OR (jd.kd_rek NOT IN ('1101', '1102', '1103', '1104', '1105') AND jd.kredit > 0)) - GROUP BY jd.kd_rek, r.nm_rek, r.tipe, r.balance - HAVING total_masuk > 0 - "; - - $stmt_masuk = $this->db()->pdo()->prepare($query_masuk); - $stmt_masuk->execute([$row['tipe'], $tgl_awal, $tgl_akhir]); - $rows_masuk = $stmt_masuk->fetchAll(); - - $row['jurnal_masuk'] = []; - foreach ($rows_masuk as $row_masuk) { - $row_masuk['kredit_all'] = $row_masuk['total_masuk']; - $row_masuk['saldo_awal'] = 0; - $row['total_masuk'] += $row_masuk['total_masuk']; - $row['jurnal_masuk'][] = $row_masuk; - $total_kredit += $row_masuk['total_masuk']; + $row['jurnal_masuk'] = array_values($agg[$tipe]['masuk'] ?? []); + usort($row['jurnal_masuk'], function ($a, $b) { + return strcmp((string) ($a['kd_rek'] ?? ''), (string) ($b['kd_rek'] ?? '')); + }); + foreach ($row['jurnal_masuk'] as $m) { + $row['total_masuk'] += (float) ($m['kredit_all'] ?? 0); } - // Arus kas keluar (transaksi yang mengurangi kas) dalam periode - $query_keluar = " - SELECT - jd.kd_rek, - r.nm_rek, - r.tipe, - r.balance, - SUM(CASE - WHEN jd.kd_rek IN ('1101', '1102', '1103', '1104', '1105') THEN jd.kredit - ELSE jd.debet - END) as total_keluar - FROM mlite_detailjurnal jd - JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek - JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal - WHERE r.tipe = ? - AND j.tgl_jurnal >= ? AND j.tgl_jurnal <= ? - AND ((jd.kd_rek IN ('1101', '1102', '1103', '1104', '1105') AND jd.kredit > 0) - OR (jd.kd_rek NOT IN ('1101', '1102', '1103', '1104', '1105') AND jd.debet > 0)) - GROUP BY jd.kd_rek, r.nm_rek, r.tipe, r.balance - HAVING total_keluar > 0 - "; - - $stmt_keluar = $this->db()->pdo()->prepare($query_keluar); - $stmt_keluar->execute([$row['tipe'], $tgl_awal, $tgl_akhir]); - $rows_keluar = $stmt_keluar->fetchAll(); - - $row['jurnal_keluar'] = []; - foreach ($rows_keluar as $row_keluar) { - $row_keluar['debet_all'] = $row_keluar['total_keluar']; - $row_keluar['saldo_awal'] = 0; - $row['total_keluar'] += $row_keluar['total_keluar']; - $row['jurnal_keluar'][] = $row_keluar; - $total_debet += $row_keluar['total_keluar']; + $row['jurnal_keluar'] = array_values($agg[$tipe]['keluar'] ?? []); + usort($row['jurnal_keluar'], function ($a, $b) { + return strcmp((string) ($a['kd_rek'] ?? ''), (string) ($b['kd_rek'] ?? '')); + }); + foreach ($row['jurnal_keluar'] as $k) { + $row['total_keluar'] += (float) ($k['debet_all'] ?? 0); } + $total_kredit += $row['total_masuk']; + $total_debet += $row['total_keluar']; + $aruskas[] = $row; } @@ -446,57 +750,69 @@ public function getNeraca() $total_hutang_jangka_panjang = 0; $total_modal = 0; - // Query untuk mendapatkan saldo awal rekening (sebelum periode) - $query_saldo_awal = " - SELECT - r.kd_rek, - r.nm_rek, - r.balance as saldo_normal, - COALESCE(SUM(jd.debet), 0) as total_debet_awal, - COALESCE(SUM(jd.kredit), 0) as total_kredit_awal, - CASE - WHEN r.balance = 'D' THEN COALESCE(SUM(jd.debet), 0) - COALESCE(SUM(jd.kredit), 0) - ELSE COALESCE(SUM(jd.kredit), 0) - COALESCE(SUM(jd.debet), 0) - END as saldo_awal - FROM mlite_rekening r - LEFT JOIN mlite_detailjurnal jd ON r.kd_rek = jd.kd_rek - LEFT JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal AND j.tgl_jurnal < ? - WHERE r.tipe IN ('N', 'M') - GROUP BY r.kd_rek, r.nm_rek, r.balance + $tahun = (int) date('Y', strtotime($tgl_awal)); + $awalTahun = $tahun . '-01-01'; + + $saldo_awal_tahun = []; + $sqlSaldoAwalTahun = " + SELECT rt.kd_rek, rt.saldo_awal + FROM mlite_rekeningtahun rt + JOIN mlite_rekening r ON r.kd_rek = rt.kd_rek + WHERE rt.thn = ? + AND r.tipe IN ('N', 'M') "; - - $stmt_awal = $this->db()->pdo()->prepare($query_saldo_awal); - $stmt_awal->execute([$tgl_awal]); - $saldo_awal_data = []; - foreach($stmt_awal->fetchAll() as $row) { - $saldo_awal_data[$row['kd_rek']] = $row['saldo_awal']; + $stSaldoAwalTahun = $this->db()->pdo()->prepare($sqlSaldoAwalTahun); + $stSaldoAwalTahun->execute([$tahun]); + foreach ($stSaldoAwalTahun->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $saldo_awal_tahun[(string) $row['kd_rek']] = (float) ($row['saldo_awal'] ?? 0); } - // Query untuk mendapatkan mutasi dalam periode - $query_mutasi = " - SELECT - r.kd_rek, - r.nm_rek, - r.balance as saldo_normal, - COALESCE(SUM(jd.debet), 0) as total_debet_periode, - COALESCE(SUM(jd.kredit), 0) as total_kredit_periode, - CASE - WHEN r.balance = 'D' THEN COALESCE(SUM(jd.debet), 0) - COALESCE(SUM(jd.kredit), 0) - ELSE COALESCE(SUM(jd.kredit), 0) - COALESCE(SUM(jd.debet), 0) - END as mutasi_periode - FROM mlite_rekening r - LEFT JOIN mlite_detailjurnal jd ON r.kd_rek = jd.kd_rek - LEFT JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal - WHERE r.tipe IN ('N', 'M') - AND j.tgl_jurnal >= ? AND j.tgl_jurnal <= ? - GROUP BY r.kd_rek, r.nm_rek, r.balance + $sqlMutasi = " + SELECT + jd.kd_rek, + COALESCE(SUM( + CASE + WHEN r.balance = 'D' THEN COALESCE(jd.debet, 0) - COALESCE(jd.kredit, 0) + ELSE COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0) + END + ), 0) AS mutasi + FROM mlite_detailjurnal jd + JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal + JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek + WHERE r.tipe IN ('N', 'M') + AND j.tgl_jurnal >= ? + AND j.tgl_jurnal < ? + GROUP BY jd.kd_rek "; - - $stmt_mutasi = $this->db()->pdo()->prepare($query_mutasi); - $stmt_mutasi->execute([$tgl_awal, $tgl_akhir]); - $mutasi_data = []; - foreach($stmt_mutasi->fetchAll() as $row) { - $mutasi_data[$row['kd_rek']] = $row['mutasi_periode']; + $stMutasiBefore = $this->db()->pdo()->prepare($sqlMutasi); + $stMutasiBefore->execute([$awalTahun, $tgl_awal]); + $mutasi_before = []; + foreach ($stMutasiBefore->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $mutasi_before[(string) $row['kd_rek']] = (float) ($row['mutasi'] ?? 0); + } + + $sqlMutasiPeriode = " + SELECT + jd.kd_rek, + COALESCE(SUM( + CASE + WHEN r.balance = 'D' THEN COALESCE(jd.debet, 0) - COALESCE(jd.kredit, 0) + ELSE COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0) + END + ), 0) AS mutasi + FROM mlite_detailjurnal jd + JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal + JOIN mlite_rekening r ON r.kd_rek = jd.kd_rek + WHERE r.tipe IN ('N', 'M') + AND j.tgl_jurnal >= ? + AND j.tgl_jurnal <= ? + GROUP BY jd.kd_rek + "; + $stMutasiPeriode = $this->db()->pdo()->prepare($sqlMutasiPeriode); + $stMutasiPeriode->execute([$tgl_awal, $tgl_akhir]); + $mutasi_periode = []; + foreach ($stMutasiPeriode->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $mutasi_periode[(string) $row['kd_rek']] = (float) ($row['mutasi'] ?? 0); } // Ambil semua rekening aktif @@ -506,8 +822,9 @@ public function getNeraca() $result = $stmt_rekening->fetchAll(); foreach($result as $rek) { // Hitung saldo akhir = saldo awal + mutasi periode - $saldo_awal = isset($saldo_awal_data[$rek['kd_rek']]) ? $saldo_awal_data[$rek['kd_rek']] : 0; - $mutasi = isset($mutasi_data[$rek['kd_rek']]) ? $mutasi_data[$rek['kd_rek']] : 0; + $kdRek = (string) ($rek['kd_rek'] ?? ''); + $saldo_awal = (float) ($saldo_awal_tahun[$kdRek] ?? 0) + (float) ($mutasi_before[$kdRek] ?? 0); + $mutasi = (float) ($mutasi_periode[$kdRek] ?? 0); $saldo_akhir = $saldo_awal + $mutasi; // Skip akun dengan saldo 0 @@ -579,7 +896,7 @@ public function getNeraca() // Hitung laba rugi periode berjalan untuk penyesuaian modal $query_labarugi = " SELECT - COALESCE(SUM(CASE WHEN r.balance = 'K' THEN jd.kredit - jd.debet ELSE jd.debet - jd.kredit END), 0) as laba_rugi + COALESCE(SUM(COALESCE(jd.kredit, 0) - COALESCE(jd.debet, 0)), 0) as laba_rugi FROM mlite_rekening r LEFT JOIN mlite_detailjurnal jd ON r.kd_rek = jd.kd_rek LEFT JOIN mlite_jurnal j ON j.no_jurnal = jd.no_jurnal @@ -594,6 +911,15 @@ public function getNeraca() // Tambahkan laba rugi ke total modal $total_modal += $laba_rugi; + if (abs((float) $laba_rugi) >= 0.01) { + $modal[] = [ + 'kd_rek' => '', + 'nm_rek' => 'Laba/Rugi Periode', + 'saldo' => (float) $laba_rugi, + 'saldo_awal' => 0, + 'mutasi' => (float) $laba_rugi + ]; + } // Hitung total aktiva dan pasiva $total_aktiva = $total_aktiva_lancar + $total_aktiva_tetap; @@ -662,6 +988,17 @@ public function postSaveSettings() public function postInsertDummyKeuangan() { + $akunKegiatan = [ + ['kegiatan' => 'Penerimaan Pasien Rawat Jalan', 'kd_rek' => '4101'], + ['kegiatan' => 'Penerimaan Pasien Rawat Inap', 'kd_rek' => '4102'], + ['kegiatan' => 'Penerimaan Penjualan Obat & BHP', 'kd_rek' => '4103'], + ['kegiatan' => 'Penerimaan Laboratorium', 'kd_rek' => '4104'], + ['kegiatan' => 'Penerimaan Radiologi', 'kd_rek' => '4105'], + ['kegiatan' => 'Pembayaran Gaji Karyawan', 'kd_rek' => '5101'], + ['kegiatan' => 'Pembelian Obat & BHP', 'kd_rek' => '5201'], + ['kegiatan' => 'Pembayaran Biaya Operasional', 'kd_rek' => '5301'], + ]; + $rekeningtahun = [ ['thn' => 2025, 'kd_rek' => '1101', 'saldo_awal' => 50000000], ['thn' => 2025, 'kd_rek' => '1201', 'saldo_awal' => 200000000], @@ -768,6 +1105,9 @@ public function postInsertDummyKeuangan() foreach ($rekeningtahun as $item) { $requiredRekening[$item['kd_rek']] = true; } + foreach ($akunKegiatan as $item) { + $requiredRekening[$item['kd_rek']] = true; + } foreach ($detailjurnal as $item) { $requiredRekening[$item['kd_rek']] = true; } @@ -802,11 +1142,31 @@ public function postInsertDummyKeuangan() } $insertedRekeningTahun = 0; + $updatedRekeningTahun = 0; foreach ($rekeningtahun as $item) { $exists = $this->db('mlite_rekeningtahun')->where('thn', $item['thn'])->where('kd_rek', $item['kd_rek'])->oneArray(); if (empty($exists)) { $this->db('mlite_rekeningtahun')->save($item); $insertedRekeningTahun++; + } else { + $current = (float) ($exists['saldo_awal'] ?? 0); + $next = (float) ($item['saldo_awal'] ?? 0); + if (abs($current - $next) >= 0.01) { + $this->db('mlite_rekeningtahun') + ->where('thn', $item['thn']) + ->where('kd_rek', $item['kd_rek']) + ->save(['saldo_awal' => $item['saldo_awal']]); + $updatedRekeningTahun++; + } + } + } + + $insertedAkunKegiatan = 0; + foreach ($akunKegiatan as $item) { + $exists = $this->db('mlite_akun_kegiatan')->where('kegiatan', $item['kegiatan'])->oneArray(); + if (empty($exists)) { + $this->db('mlite_akun_kegiatan')->save($item); + $insertedAkunKegiatan++; } } @@ -835,7 +1195,7 @@ public function postInsertDummyKeuangan() } $this->db()->pdo()->commit(); - $this->notify('success', 'Data dummy keuangan diproses. Insert baru: rekening tahun '.$insertedRekeningTahun.', jurnal '.$insertedJurnal.', detail jurnal '.$insertedDetail.'.'); + $this->notify('success', 'Data dummy keuangan diproses. Rekening tahun: insert '.$insertedRekeningTahun.', update '.$updatedRekeningTahun.'. Akun kegiatan: insert '.$insertedAkunKegiatan.'. Jurnal: insert '.$insertedJurnal.'. Detail jurnal: insert '.$insertedDetail.'.'); } catch (\Exception $e) { $this->db()->pdo()->rollBack(); $this->notify('failure', 'Insert data dummy keuangan gagal: '.$e->getMessage()); diff --git a/plugins/keuangan/README.md b/plugins/keuangan/README.md index c24e88caa..23035c5c9 100644 --- a/plugins/keuangan/README.md +++ b/plugins/keuangan/README.md @@ -66,6 +66,87 @@ Admin mengelola master data dan konfigurasi: - Atur akun default modul keuangan (mis. akun kredit layanan). - Simpan perubahan konfigurasi. +## Simulasi Penggunaan Tanpa Data Dummy (Tahun 2026) + +Bagian ini contoh alur pengisian jika Anda memulai dari nol (tanpa “Data Dummy Keuangan”) untuk tahun 2026. + +### A. Pengaturan Akun Rekening + +Buat minimal beberapa akun berikut: + +- **1101 Kas Umum** (Aktiva; tipe: N; saldo normal/balance: D) +- **1201 Bank** (opsional) (Aktiva; tipe: N; balance: D) +- **3101 Modal Disetor** (Modal; tipe: N; balance: K) +- **4101 Pendapatan Jasa** (Pendapatan; tipe: R; balance: K) +- **5101 Biaya Operasional** (Beban; tipe: R; balance: D) +- **2101 Hutang Usaha** (opsional) (Hutang; tipe: N; balance: K) + +Catatan: +- Tipe **N** = akun Neraca (Aktiva/Hutang/Modal). +- Tipe **R** = akun Laba-Rugi (Pendapatan/Beban). + +### B. Pengaturan Rekening Tahun (Saldo Awal 2026) + +Menu: **Keuangan → Rekening Tahun** → pilih tahun **2026**. + +Contoh saldo awal yang seimbang: +- 1101 Kas Umum = 10.000.000 +- 3101 Modal Disetor = 10.000.000 + +Jika saldo awal tidak seimbang, laporan Neraca akan menampilkan peringatan selisih. + +### C. Pengaturan Kegiatan (Pengaturan Rekening / Input Kegiatan) + +Menu: **Keuangan → Pengaturan Rekening**. + +Tujuan kegiatan adalah pengelompokan aktivitas (umumnya untuk pelaporan arus kas/kegiatan). + +Contoh set minimal: +- Penerimaan Jasa → mapping rekening **4101** +- Biaya Operasional → mapping rekening **5101** +- Pembelian Kredit (opsional) → mapping rekening **2101** atau rekening beban terkait + +### D. Contoh Pengisian Jurnal (Dana Masuk & Dana Keluar) + +Menu: **Keuangan → Posting Jurnal** (atau input jurnal sesuai alur di instalasi Anda). + +Pastikan setiap jurnal memiliki minimal 2 baris dan **Total Debet = Total Kredit**. + +#### 1) Dana Masuk (Penerimaan Jasa Tunai) + +- Tanggal: 2026-01-05 +- Keterangan: Penerimaan jasa tunai +- Detail: + - Debet 1101 Kas Umum = 2.000.000 + - Kredit 4101 Pendapatan Jasa = 2.000.000 + +#### 2) Dana Keluar (Biaya Operasional Dibayar Tunai) + +- Tanggal: 2026-01-06 +- Keterangan: Bayar listrik/ATK +- Detail: + - Debet 5101 Biaya Operasional = 500.000 + - Kredit 1101 Kas Umum = 500.000 + +#### 3) Dana Keluar (Pembelian Kredit / Timbul Hutang) (Opsional) + +- Tanggal: 2026-01-10 +- Keterangan: Pembelian perlengkapan secara kredit +- Detail: + - Debet 5101 Biaya Operasional = 1.200.000 + - Kredit 2101 Hutang Usaha = 1.200.000 + +### E. Cara Validasi Output Laporan + +- **Buku Besar**: + - Pilih 1101 untuk melihat saldo kas (saldo awal + mutasi). + - Pilih 4101 untuk melihat total pendapatan. + - Pilih 5101 untuk melihat total biaya. +- **Neraca Keuangan** (periode 01-01-2026 s/d tanggal akhir): + - Aktiva harus sama dengan Pasiva + Modal (laba/rugi periode muncul sebagai penyesuaian modal). +- **Cash Flow**: + - Jurnal “Dana Masuk” muncul di arus masuk, jurnal “Dana Keluar” muncul di arus keluar. + ## Catatan - Gunakan data akun dan jurnal yang valid agar laporan Cash Flow dan Neraca akurat. diff --git a/plugins/keuangan/view/admin/buku.besar.html b/plugins/keuangan/view/admin/buku.besar.html index 41b41e9df..e2368b480 100644 --- a/plugins/keuangan/view/admin/buku.besar.html +++ b/plugins/keuangan/view/admin/buku.besar.html @@ -8,11 +8,7 @@
- {if: isset($_GET['tgl_awal']) && isset($_GET['tgl_akhir'])} - Print - {else} - Print - {/if} + Print
@@ -35,6 +31,12 @@ {/loop} +
  • + +
  • @@ -51,32 +53,75 @@

    Kelola Buku Besar

    No. Jurnal Tanggal + {if: empty($kd_rek_filter)} + Akun + {/if} Keterangan Debet Kredit Saldo + Saldo Normal {if: $bukubesar} {loop: $bukubesar} + {if: isset($value.row_type) && $value.row_type == 'saldo_akhir'} + + {else} + {if: isset($value.row_type) && $value.row_type == 'saldo_awal'} + + {else} + {/if} + {/if} {?=isset_or($value.no_jurnal)?} {?=isset_or($value.tgl_jurnal)?} - {?=isset_or($value.keterangan)?} + {if: empty($kd_rek_filter)} + {?=isset_or($value.kd_rek)?} - {?=isset_or($value.nm_rek)?} + {/if} + + {if: isset($value.row_type) && ($value.row_type == 'saldo_awal' || $value.row_type == 'saldo_akhir')} + {?=isset_or($value.keterangan)?} + {else} + {?=isset_or($value.keterangan)?} + {/if} + {?= number_format($value.debet,2,',','.')?} {?= number_format($value.kredit,2,',','.')?} {?= number_format($value.saldo,2,',','.')?} + {?=isset_or($value.saldo_sisi)?} {?= number_format((float) isset_or($value.saldo_normal),2,',','.')?} {/loop} {else} - Belum ada data buku besar. + {if: empty($kd_rek_filter)} + Belum ada data buku besar. + {else} + Belum ada data buku besar. + {/if} {/if}
  • + {if: isset($summary)} +
    +
    Ringkasan Buku Besar
    +
    Jumlah rekening: {?= (int) isset_or($summary.rekening_count)?}
    +
    Total debet: {?= number_format((float) isset_or($summary.total_debet),2,',','.')?}
    +
    Total kredit: {?= number_format((float) isset_or($summary.total_kredit),2,',','.')?}
    + {if: $show_saldo_akhir == 1} + {if: empty($kd_rek_filter)} +
    Total saldo akhir (D): {?= number_format((float) isset_or($summary.saldo_akhir_d),2,',','.')?}
    +
    Total saldo akhir (K): {?= number_format((float) isset_or($summary.saldo_akhir_k),2,',','.')?}
    +
    Saldo akhir (net): {?=isset_or($summary.saldo_akhir_net_side)?} {?= number_format((float) isset_or($summary.saldo_akhir_net_amount),2,',','.')?}
    + {else} +
    Saldo akhir: {?=isset_or($summary.saldo_akhir_side)?} {?= number_format((float) isset_or($summary.saldo_akhir_amount),2,',','.')?}
    + {/if} + {/if} +
    + {/if}
    @@ -87,5 +132,9 @@

    Kelola Buku Besar

    format: 'YYYY-MM-DD', locale: 'id' }); + + $(document).on('click', '.dropdown-menu', function (e) { + e.stopPropagation(); + }); }); diff --git a/plugins/keuangan/view/admin/buku.besar.print.html b/plugins/keuangan/view/admin/buku.besar.print.html index 338eb9925..da1586eac 100644 --- a/plugins/keuangan/view/admin/buku.besar.print.html +++ b/plugins/keuangan/view/admin/buku.besar.print.html @@ -43,11 +43,7 @@

    {?=isset_or($settings.nama_instansi)?}


    Laporan Buku Besar
    - {if: isset($_GET['tgl_awal']) && isset($_GET['tgl_akhir'])} - Periode {?=date('d-m-Y', strtotime($_GET['tgl_awal']))?} s/d {?=date('d-m-Y', strtotime($_GET['tgl_akhir']))?} - {else} - Periode {?=date('d-m-Y')?} s/d {?=date('d-m-Y')?} - {/if} + Periode {?=date('d-m-Y', strtotime($_GET['tgl_awal'] ?? date('Y-m-d')))?} s/d {?=date('d-m-Y', strtotime($_GET['tgl_akhir'] ?? date('Y-m-d')))?}

    @@ -56,10 +52,14 @@

    {?=isset_or($settings.nama_instansi)?}

    No. Jurnal Tanggal + {if: empty($kd_rek_filter)} + Akun + {/if} Keterangan Debet Kredit Saldo + Saldo Normal @@ -67,15 +67,42 @@

    {?=isset_or($settings.nama_instansi)?}

    {?=isset_or($value.no_jurnal)?} {?=isset_or($value.tgl_jurnal)?} - {?=isset_or($value.keterangan)?} + {if: empty($kd_rek_filter)} + {?=isset_or($value.kd_rek)?} - {?=isset_or($value.nm_rek)?} + {/if} + + {if: isset($value.row_type) && ($value.row_type == 'saldo_awal' || $value.row_type == 'saldo_akhir')} + {?=isset_or($value.keterangan)?} + {else} + {?=isset_or($value.keterangan)?} + {/if} + {?= number_format($value.debet,2,',','.')?} {?= number_format($value.kredit,2,',','.')?} {?= number_format($value.saldo,2,',','.')?} + {?=isset_or($value.saldo_sisi)?} {?= number_format((float) isset_or($value.saldo_normal),2,',','.')?} {/loop}
    + {if: isset($summary)} +
    +
    Ringkasan Buku Besar
    +
    Jumlah rekening: {?= (int) isset_or($summary.rekening_count)?}
    +
    Total debet: {?= number_format((float) isset_or($summary.total_debet),2,',','.')?}
    +
    Total kredit: {?= number_format((float) isset_or($summary.total_kredit),2,',','.')?}
    + {if: $show_saldo_akhir == 1} + {if: empty($kd_rek_filter)} +
    Total saldo akhir (D): {?= number_format((float) isset_or($summary.saldo_akhir_d),2,',','.')?}
    +
    Total saldo akhir (K): {?= number_format((float) isset_or($summary.saldo_akhir_k),2,',','.')?}
    +
    Saldo akhir (net): {?=isset_or($summary.saldo_akhir_net_side)?} {?= number_format((float) isset_or($summary.saldo_akhir_net_amount),2,',','.')?}
    + {else} +
    Saldo akhir: {?=isset_or($summary.saldo_akhir_side)?} {?= number_format((float) isset_or($summary.saldo_akhir_amount),2,',','.')?}
    + {/if} + {/if} +
    + {/if} diff --git a/plugins/keuangan/view/admin/rekening.tahun.html b/plugins/keuangan/view/admin/rekening.tahun.html index 2ca9daa37..97ab1e2c1 100644 --- a/plugins/keuangan/view/admin/rekening.tahun.html +++ b/plugins/keuangan/view/admin/rekening.tahun.html @@ -32,6 +32,16 @@

    Input Saldo Awal

    +
    +
    + + + + + + +
    +
    diff --git a/plugins/surat/Admin.php b/plugins/surat/Admin.php index 943a5b6c2..58a502591 100644 --- a/plugins/surat/Admin.php +++ b/plugins/surat/Admin.php @@ -69,6 +69,7 @@ public function anyRujukan($page = 1) if (count($rows)) { foreach ($rows as $row) { $row = htmlspecialchars_array($row); + $row['printURL'] = url([ADMIN, 'surat', 'suratrujukan', convertNorawat($row['no_rawat'])]); $row['editURL'] = url([ADMIN, 'surat', 'rujukanedit', $row['id']]); $row['deleteURL'] = url([ADMIN, 'surat', 'rujukanhapus', $row['id']]); $this->assign['list'][] = $row; @@ -78,7 +79,9 @@ public function anyRujukan($page = 1) $this->assign['searchURL'] = url([ADMIN, 'surat', 'rujukan']); $this->assign['addURL'] = url([ADMIN, 'surat', 'rujukanadd']); $this->assign['phrase'] = $phrase; - return $this->draw('rujukan.manage.html', ['rujukan' => htmlspecialchars_array($this->assign)]); + $assign = htmlspecialchars_array($this->assign); + $assign['pagination'] = $this->assign['pagination']; + return $this->draw('rujukan.manage.html', ['rujukan' => $assign]); } public function getRujukanAdd() @@ -220,6 +223,7 @@ public function anySakit($page = 1) if (count($rows)) { foreach ($rows as $row) { $row = htmlspecialchars_array($row); + $row['printURL'] = url([ADMIN, 'surat', 'suratsakit', convertNorawat($row['no_rawat'])]); $row['editURL'] = url([ADMIN, 'surat', 'sakitedit', $row['id']]); $row['deleteURL'] = url([ADMIN, 'surat', 'sakithapus', $row['id']]); $this->assign['list'][] = $row; @@ -229,7 +233,9 @@ public function anySakit($page = 1) $this->assign['searchURL'] = url([ADMIN, 'surat', 'sakit']); $this->assign['addURL'] = url([ADMIN, 'surat', 'sakitadd']); $this->assign['phrase'] = $phrase; - return $this->draw('sakit.manage.html', ['sakit' => htmlspecialchars_array($this->assign)]); + $assign = htmlspecialchars_array($this->assign); + $assign['pagination'] = $this->assign['pagination']; + return $this->draw('sakit.manage.html', ['sakit' => $assign]); } public function getSakitAdd() @@ -369,6 +375,7 @@ public function anySehat($page = 1) if (count($rows)) { foreach ($rows as $row) { $row = htmlspecialchars_array($row); + $row['printURL'] = url([ADMIN, 'surat', 'suratsehat', convertNorawat($row['no_rawat'])]); $row['editURL'] = url([ADMIN, 'surat', 'sehatedit', $row['id']]); $row['deleteURL'] = url([ADMIN, 'surat', 'sehathapus', $row['id']]); $this->assign['list'][] = $row; @@ -378,7 +385,9 @@ public function anySehat($page = 1) $this->assign['searchURL'] = url([ADMIN, 'surat', 'sehat']); $this->assign['addURL'] = url([ADMIN, 'surat', 'sehatadd']); $this->assign['phrase'] = $phrase; - return $this->draw('sehat.manage.html', ['sehat' => htmlspecialchars_array($this->assign)]); + $assign = htmlspecialchars_array($this->assign); + $assign['pagination'] = $this->assign['pagination']; + return $this->draw('sehat.manage.html', ['sehat' => $assign]); } public function getSehatAdd() @@ -526,4 +535,4 @@ private function _addHeaderFiles() $this->core->addJS(url('assets/jscripts/jquery.dataTables.min.js')); $this->core->addJS(url('assets/jscripts/dataTables.bootstrap.min.js')); } -} \ No newline at end of file +} diff --git a/plugins/surat/view/admin/rujukan.manage.html b/plugins/surat/view/admin/rujukan.manage.html index c3e2a6463..9aa64b10d 100644 --- a/plugins/surat/view/admin/rujukan.manage.html +++ b/plugins/surat/view/admin/rujukan.manage.html @@ -45,6 +45,7 @@

    Jumlah: {?=count($rujukan.totalRe

    @@ -62,4 +63,4 @@

    Jumlah: {?=count($rujukan.totalRe - \ No newline at end of file + diff --git a/plugins/surat/view/admin/sakit.manage.html b/plugins/surat/view/admin/sakit.manage.html index d1561f20a..7b047d7ad 100644 --- a/plugins/surat/view/admin/sakit.manage.html +++ b/plugins/surat/view/admin/sakit.manage.html @@ -45,6 +45,7 @@

    Jumlah: {?=count($sakit.totalReco

    @@ -62,4 +63,4 @@

    Jumlah: {?=count($sakit.totalReco - \ No newline at end of file + diff --git a/plugins/surat/view/admin/sehat.manage.html b/plugins/surat/view/admin/sehat.manage.html index 4ca3a9ffe..828863094 100644 --- a/plugins/surat/view/admin/sehat.manage.html +++ b/plugins/surat/view/admin/sehat.manage.html @@ -45,6 +45,7 @@

    Jumlah: {?=count($sehat.totalReco

    @@ -62,4 +63,4 @@

    Jumlah: {?=count($sehat.totalReco - \ No newline at end of file + diff --git a/plugins/surat/view/admin/surat.sakit.html b/plugins/surat/view/admin/surat.sakit.html index bb97a3f79..56609de38 100644 --- a/plugins/surat/view/admin/surat.sakit.html +++ b/plugins/surat/view/admin/surat.sakit.html @@ -13,7 +13,7 @@ font-family: georgia; } @media print { - #saveButton, #printPageButton { + #saveButton, #printPageButton, #signButton { display: none; } textarea { @@ -173,4 +173,4 @@

    ▣ Simpan - + diff --git a/systems/upgrade.php b/systems/upgrade.php index f8ae963be..e3a31e3e1 100644 --- a/systems/upgrade.php +++ b/systems/upgrade.php @@ -1874,8 +1874,8 @@ function rrmdir($dir) `keterangan` TEXT DEFAULT NULL, `manufacturer` TEXT DEFAULT NULL, `model` TEXT DEFAULT NULL, - 'manufacture_date' TEXT NOT NULL, - 'expiration_date' TEXT NOT NULL + `manufacture_date` TEXT DEFAULT NULL, + `expiration_date` TEXT DEFAULT NULL );"); $this->core->db()->pdo()->exec("CREATE INDEX IF NOT EXISTS `idx_bpjs_emr_device_nama_alkes` ON `mlite_bpjs_emr_device` (`nama_alkes`);"); $this->core->db()->pdo()->exec("CREATE TABLE IF NOT EXISTS `mlite_bpjs_emr_logs` ( @@ -1950,8 +1950,8 @@ function rrmdir($dir) try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur` ADD COLUMN `master_device_id` INTEGER DEFAULT NULL;"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur_ranap` ADD COLUMN `master_device_id` INTEGER DEFAULT NULL;"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_operasi` ADD COLUMN `master_device_id` INTEGER DEFAULT NULL;"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` TEXT NOT NULL;"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` TEXT NOT NULL;"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` TEXT DEFAULT NULL;"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` TEXT DEFAULT NULL;"); } catch (\Exception $e) {} } else { // Kapabilitas MySQL sejak 6.2.0 $this->core->db()->pdo()->exec("CREATE TABLE IF NOT EXISTS `mlite_mini_pacs_study` ( @@ -2121,8 +2121,8 @@ function rrmdir($dir) `keterangan` text DEFAULT NULL, `manufacturer` varchar(255) DEFAULT NULL, `model` varchar(255) DEFAULT NULL, - 'manufacture_date' DATE NOT NULL, - 'expiration_date' DATE NOT NULL, + `manufacture_date` DATE DEFAULT NULL, + `expiration_date` DATE DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uq_device_id` (`device_id`), KEY `idx_nama_alkes` (`nama_alkes`) @@ -2148,8 +2148,8 @@ function rrmdir($dir) try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur_ranap` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_operasi` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` DATE NOT NULL"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` DATE NOT NULL"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` DATE DEFAULT NULL"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` DATE DEFAULT NULL"); } catch (\Exception $e) {} $this->core->db()->pdo()->exec("CREATE TABLE IF NOT EXISTS `mlite_bpjs_emr_uuid_condition` ( `kd_penyakit` varchar(15) NOT NULL, `uuid` varchar(200) DEFAULT NULL @@ -2172,7 +2172,7 @@ function rrmdir($dir) PRIMARY KEY (`id`), KEY `ref_idx` (`ref_type`,`ref_id`), KEY `hash_idx` (`signature_hash`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;"); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;"); $this->core->db()->pdo()->exec("CREATE TABLE IF NOT EXISTS `mlite_sertisign_webhook` ( `id` int NOT NULL AUTO_INCREMENT, `transaction_id` varchar(100) NOT NULL, @@ -2183,7 +2183,7 @@ function rrmdir($dir) PRIMARY KEY (`id`), KEY `transaction_idx` (`transaction_id`), KEY `status_idx` (`status`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;"); + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;"); $this->core->db()->pdo()->exec("CREATE TABLE IF NOT EXISTS `mlite_mapping_snomed_icd` ( `id` int NOT NULL AUTO_INCREMENT, `no_rawat` varchar(20) NOT NULL, @@ -2219,16 +2219,16 @@ function rrmdir($dir) try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur` ADD COLUMN `master_device_id` INTEGER DEFAULT NULL;"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur_ranap` ADD COLUMN `master_device_id` INTEGER DEFAULT NULL;"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_operasi` ADD COLUMN `master_device_id` INTEGER DEFAULT NULL;"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` TEXT NOT NULL;"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` TEXT NOT NULL;"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` TEXT DEFAULT NULL;"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` TEXT DEFAULT NULL;"); } catch (\Exception $e) {} } else { try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_lab` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_radiologi` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_prosedur_ranap` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_mapping_operasi` ADD COLUMN `master_device_id` int DEFAULT NULL"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` DATE NOT NULL"); } catch (\Exception $e) {} - try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` DATE NOT NULL"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `manufacture_date` DATE DEFAULT NULL"); } catch (\Exception $e) {} + try { $this->core->db()->pdo()->exec("ALTER TABLE `mlite_bpjs_emr_device` ADD COLUMN `expiration_date` DATE DEFAULT NULL"); } catch (\Exception $e) {} } $return = '6.3.1'; break;

    {?=isset_or($value.diagnosa)?} {?=isset_or($value.dokter)?} + Cetak Edit Hapus {?=isset_or($value.lama_angka)?} {?=isset_or($value.lama_huruf)?} {?=isset_or($value.dokter)?} + Cetak Edit Hapus {?=isset_or($value.keperluan)?} {?=isset_or($value.dokter)?} + Cetak Edit Hapus