AddFont('Arial_Narrow', '', 'Arial_Narrow.php'); // regular $this->AddFont('Arial_Narrow', 'B', 'Arial_Narrow_B.php'); // bold } public function Footer() { $pageW = $this->GetPageWidth() - 30; // margin 15+15 // -- Posisi: 20mm dari bawah halaman ---------------------------------- $this->SetY(-20); // -- Baris 1: Print Oleh (kiri) | Nomor Halaman (tengah) --------------- $colSide = ($pageW - 40) / 2; $colCenter = 40; $this->SetFont('Arial_Narrow', '', 7); $this->Cell($colSide, 4, 'Print Oleh : ' . $this->printUsername, 0, 0, 'L'); $this->Cell($colCenter, 4, $this->PageNo() . ' / {nb}', 0, 0, 'C'); $this->Cell($colSide, 4, '', 0, 1, 'R'); // -- Baris 2: Tgl Print (kiri) ------------------------------------------ $this->SetFont('Arial_Narrow', '', 7); $this->Cell($colSide, 4, 'Tgl Print : ' . $this->printDate, 0, 0, 'L'); $this->Cell($colCenter + $colSide, 4, '', 0, 1, 'L'); } } // ============================================================================= // Controller // ============================================================================= class Rpt_pemakaian_item_divisi extends MY_Controller { // -- Properti bersama antar fungsi PDF ------------------------------------- /** @var PemakaianItemDivisiFpdf */ private $_pdf; private $_pageW; private $_start_date; private $_end_date; private $_division_id; private $_division_name; private $_username; public function __construct() { parent::__construct(); } public function index() { echo "Pemakaian Item per Divisi Report API"; } // ========================================================================= // ENDPOINT: pdf // GET/POST: start_date, end_date, division_id (opsional), username (opsional) // ========================================================================= public function pdf() { try { $start_date = trim($this->input->get_post('start_date') ?? ''); $end_date = trim($this->input->get_post('end_date') ?? ''); $division_id = intval($this->input->get_post('division_id')); $username = trim($this->input->get_post('username') ?? ''); if ($start_date === '' || $end_date === '') { $this->sys_error("Periode tanggal harus diisi"); exit; } // -- Ambil data dari DB -------------------------------------------- $data = $this->_get_data($start_date, $end_date, $division_id); if (empty($data)) { $this->sys_error("Tidak ada data pemakaian item pada periode tersebut"); exit; } $this->_start_date = $start_date; $this->_end_date = $end_date; $this->_division_id = $division_id; // Ambil nama divisi jika dipilih if ($division_id > 0) { $div = $this->_get_division($division_id); $this->_division_name = $div ? $div['DivisionName'] : ''; } else { $this->_division_name = 'Semua Divisi'; } // -- Inisialisasi FPDF custom -------------------------------------- $this->_pdf = new PemakaianItemDivisiFpdf('P', 'mm', 'A4'); $this->_pdf->printUsername = $username !== '' ? $username : '-'; $this->_pdf->printDate = date('d-m-Y H:i:s'); $this->_pageW = $this->_pdf->GetPageWidth() - 30; $this->_username = $this->_pdf->printUsername; $this->_pdf->AliasNbPages(); // aktifkan alias total halaman $this->_pdf->SetMargins(15, 15, 15); $this->_pdf->SetAutoPageBreak(true, 22); // 22mm ruang footer di bawah $this->_pdf->AddPage(); // -- Susun isi halaman --------------------------------------------- $this->_pdf_header(); $this->_pdf_data($data); $this->_pdf_terms(); // -- Output -------------------------------------------------------- $filename = 'PID_' . date('Ymd') . '.pdf'; header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="' . $filename . '"'); header('Cache-Control: private, max-age=0, must-revalidate'); header('Pragma: public'); echo $this->_pdf->Output('S'); } catch (Exception $exc) { $this->sys_error($exc->getMessage()); } } // ========================================================================= // DATABASE: Ambil data divisi // ========================================================================= private function _get_division($id) { $sql = " SELECT DivisionID, DivisionCode, DivisionName FROM division WHERE DivisionID = ? AND DivisionIsActive = 'Y' LIMIT 1 "; $qry = $this->db->query($sql, array($id)); return $qry && $qry->num_rows() > 0 ? $qry->row_array() : null; } // ========================================================================= // DATABASE: Ambil data pemakaian item per divisi // ========================================================================= private function _get_data($start_date, $end_date, $division_id = 0) { $sql_where = " WHERE iu.ItemUsedIsActive = 'Y' AND iu.ItemUsedIsConfirm = 'Y' AND iu.ItemUsedDate BETWEEN ? AND ? "; $params = array($start_date, $end_date); if ($division_id > 0) { // Filter by division melalui item_usage $sql_where .= " AND iu.ItemUsedItemUsageID IN ( SELECT ItemUsageID FROM item_usage WHERE ItemUsageDivisionID = ? AND ItemUsageIsActive = 'Y' )"; $params[] = $division_id; } $sql = " SELECT d.DivisionID, d.DivisionCode, d.DivisionName, iu.ItemUsedID, iu.ItemUsedDate, iu.ItemUsedNumber, iu.ItemUsedBatchNo, iu.ItemUsedQty, iu.ItemUsedPrice, iu.ItemUsedTotal, item.M_ItemID, item.M_ItemCode, item.M_ItemDesc, iunit.ItemUnitName, iunit.ItemUnitCode, us.ItemUsageID, us.ItemUsageDivisionID FROM item_used iu JOIN item_usage us ON us.ItemUsageID = iu.ItemUsedItemUsageID AND us.ItemUsageIsActive = 'Y' JOIN division d ON d.DivisionID = us.ItemUsageDivisionID AND d.DivisionIsActive = 'Y' JOIN m_item item ON item.M_ItemID = iu.ItemUsedM_ItemID AND item.M_ItemIsActive = 'Y' JOIN itemunit iunit ON iunit.ItemUnitID = iu.ItemUsedItemUnitID AND iunit.ItemUnitIsActive = 'Y' $sql_where ORDER BY d.DivisionCode ASC, d.DivisionName ASC, iu.ItemUsedDate ASC, iu.ItemUsedNumber ASC "; $qry = $this->db->query($sql, $params); return $qry ? $qry->result_array() : array(); } // ========================================================================= // PDF SECTION: Header — Judul + Informasi Laporan // ========================================================================= private function _pdf_header() { $pdf = $this->_pdf; $pageW = $this->_pageW; // -- Judul utama ------------------------------------------------------- $pdf->SetFont('Arial_Narrow', 'B', 14); $pdf->Cell($pageW, 8, 'LAPORAN PEMAKAIAN ITEM PER DIVISI', 0, 1, 'L'); $pdf->SetDrawColor(0, 0, 0); $pdf->SetLineWidth(0.5); $pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY()); $pdf->Ln(2); $startY = $pdf->GetY(); $halfW = $pageW / 2; $lblW = 30; $valW = $halfW - $lblW - 4; // -- Kolom Kiri -------------------------------------------------------- $pdf->SetY($startY); $leftItems = array( array('Periode Awal', $this->_fmt_date($this->_start_date), false), array('Periode Akhir', $this->_fmt_date($this->_end_date), false), array('Divisi', $this->_division_name, true), ); foreach ($leftItems as $item) { $pdf->SetX(15); $pdf->SetFont('Arial_Narrow', '', 9); $pdf->Cell($lblW, 5, $item[0], 0, 0, 'L'); $pdf->Cell(4, 5, ':', 0, 0, 'C'); if ($item[2]) { $pdf->SetFont('Arial_Narrow', 'B', 9); } else { $pdf->SetFont('Arial_Narrow', '', 9); } $pdf->Cell($valW, 5, $item[1], 0, 1, 'L'); } $leftY = $pdf->GetY(); // -- Kolom Kanan ------------------------------------------------------- $pdf->SetY($startY); $rightX = 15 + $halfW; $rightItems = array( array('Print Oleh', $this->_username), array('Tgl Print', $this->_pdf->printDate), ); foreach ($rightItems as $item) { $pdf->SetX($rightX); $pdf->SetFont('Arial_Narrow', '', 9); $pdf->Cell($lblW, 5, $item[0], 0, 0, 'L'); $pdf->Cell(4, 5, ':', 0, 0, 'C'); $pdf->SetFont('Arial_Narrow', '', 9); $pdf->Cell($valW, 5, $item[1], 0, 1, 'L'); } $rightY = $pdf->GetY(); // Posisikan Y ke yang paling bawah + margin $pdf->SetY(max($leftY, $rightY) + 4); } // ========================================================================= // PDF SECTION: Data — Tabel pemakaian item per divisi // ========================================================================= private function _pdf_data($data) { $pdf = $this->_pdf; $pageW = $this->_pageW; // -- Definisi kolom: [label, lebar, align] ----------------------------- // Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan) // 8 + 8 + 68 + 15 + 15 + 22 + 22 + 22 = 180 $cols = array( array('No', 6, 'C'), array('Tgl', 16, 'C'), array('Nama Item', 62, 'L'), array('Batch No', 24, 'C'), array('Unit', 14, 'C'), array('Qty', 10, 'R'), array('Harga', 18, 'R'), array('Total', 30, 'R'), ); $no = 1; $prevDivID = null; $divTotalQty = 0; $divTotalVal = 0; $grandTotalQty = 0; $grandTotalVal = 0; // Simpan posisi awal Y untuk setiap divisi — dipakai untuk bounding box $divStartY = 0; foreach ($data as $d) { $divID = $d['DivisionID']; // -- Jika berganti divisi, cetak subtotal divisi sebelumnya ---------- if ($prevDivID !== null && $divID !== $prevDivID) { $this->_pdf_divisi_subtotal($divTotalQty, $divTotalVal); $grandTotalQty += $divTotalQty; $grandTotalVal += $divTotalVal; $divTotalQty = 0; $divTotalVal = 0; } // -- Jika divisi baru, cetak header nama divisi --------------------- if ($prevDivID === null || $divID !== $prevDivID) { // Cek sisa ruang: butuh minimal 8+7+6*n+8 ˜ 40mm untuk header+1 baris+subtotal if ($pdf->GetY() > 230) { $pdf->AddPage(); } $divStartY = $pdf->GetY(); // Nama Divisi $pdf->SetFillColor(50, 80, 130); $pdf->SetTextColor(255, 255, 255); $pdf->SetFont('Arial_Narrow', 'B', 10); $pdf->Cell($pageW, 7, ' ' . $d['DivisionName'] . ' (' . $d['DivisionCode'] . ')', 1, 1, 'L', true); $pdf->SetTextColor(0, 0, 0); // Header kolom $pdf->SetLineWidth(0.3); $pdf->SetFont('Arial_Narrow', 'B', 7); $pdf->SetFillColor(220, 220, 220); $pdf->SetDrawColor(0, 0, 0); foreach ($cols as $c) { $pdf->Cell($c[1], 6, $c[0], 1, 0, 'C', true); } $pdf->Ln(); $no = 1; } // -- Cek apakah perlu halaman baru (min 12mm untuk 1 baris + subtotal) - if ($pdf->GetY() > 258) { $pdf->AddPage(); // Ulang header kolom di halaman baru $pdf->SetLineWidth(0.3); $pdf->SetFont('Arial_Narrow', 'B', 7); $pdf->SetFillColor(220, 220, 220); $pdf->SetDrawColor(0, 0, 0); foreach ($cols as $c) { $pdf->Cell($c[1], 6, $c[0], 1, 0, 'C', true); } $pdf->Ln(); } // -- Baris data item ----------------------------------------------- $pdf->SetLineWidth(0.2); $pdf->SetFont('Arial_Narrow', '', 7.5); $pdf->SetFillColor(255, 255, 255); $itemDesc = $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'); $batchNo = $d['ItemUsedBatchNo'] ?: '-'; $qty = floatval($d['ItemUsedQty']); $price = floatval($d['ItemUsedPrice']); $total = floatval($d['ItemUsedTotal']); // Simpan posisi Y sebelum cell untuk wrap detection $beforeY = $pdf->GetY(); $pdf->Cell($cols[0][1], 5, $no, 1, 0, 'C'); $pdf->Cell($cols[1][1], 5, $this->_fmt_date($d['ItemUsedDate']), 1, 0, 'C'); $pdf->Cell($cols[2][1], 5, $itemDesc, 1, 0, 'L'); $pdf->Cell($cols[3][1], 5, $batchNo, 1, 0, 'C'); $pdf->Cell($cols[4][1], 5, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C'); $pdf->Cell($cols[5][1], 5, $this->_fmt_qty($qty), 1, 0, 'R'); $pdf->Cell($cols[6][1], 5, $this->_fmt_rp($price), 1, 0, 'R'); $pdf->Cell($cols[7][1], 5, $this->_fmt_rp($total), 1, 0, 'R'); $pdf->Ln(); $divTotalQty += $qty; $divTotalVal += $total; $prevDivID = $divID; $no++; } // -- Subtotal divisi terakhir ----------------------------------------- if ($prevDivID !== null) { $this->_pdf_divisi_subtotal($divTotalQty, $divTotalVal); $grandTotalQty += $divTotalQty; $grandTotalVal += $divTotalVal; } // -- Grand Total ------------------------------------------------------ $pdf->Ln(3); // Cek ruang untuk grand total if ($pdf->GetY() > 270) { $pdf->AddPage(); } $pdf->SetLineWidth(0.4); $pdf->SetFont('Arial_Narrow', 'B', 10); $pdf->SetFillColor(180, 200, 220); $pdf->SetDrawColor(0, 0, 0); $spanSum = array_sum(array_column(array_slice($cols, 0, 5), 1)); $pdf->Cell($spanSum, 7, 'GRAND TOTAL', 1, 0, 'R', true); $pdf->Cell($cols[5][1], 7, $this->_fmt_qty($grandTotalQty), 1, 0, 'R', true); $pdf->Cell($cols[6][1], 7, '', 1, 0, 'R', true); $pdf->Cell($cols[7][1], 7, $this->_fmt_rp($grandTotalVal), 1, 0, 'R', true); $pdf->Ln(); $pdf->Ln(4); // -- Ringkasan Akhir -------------------------------------------------- $summaryX = 15 + $pageW - 90; $cW1 = 50; $cW2 = 40; $pdf->SetFont('Arial_Narrow', '', 9); $pdf->SetX($summaryX); $pdf->Cell($cW1, 5, 'Total Item Digunakan', 0, 0, 'L'); $pdf->SetFont('Arial_Narrow', 'B', 9); $pdf->Cell($cW2, 5, $this->_fmt_qty($grandTotalQty) . ' ' . 'pcs', 0, 1, 'R'); $pdf->SetFont('Arial_Narrow', '', 9); $pdf->SetX($summaryX); $pdf->Cell($cW1, 5, 'Total Nilai Pemakaian', 0, 0, 'L'); $pdf->SetFont('Arial_Narrow', 'B', 9); $pdf->Cell($cW2, 5, $this->_fmt_rp($grandTotalVal), 0, 1, 'R'); $pdf->Ln(4); } // ========================================================================= // PDF SUB-SECTION: Subtotal per Divisi // ========================================================================= private function _pdf_divisi_subtotal($qty, $val) { $pdf = $this->_pdf; $pageW = $this->_pageW; // Gunakan lebar kolom yang sama persis dengan _pdf_data() $cols = array( array('No', 6, 'C'), array('Tgl', 16, 'C'), array('Nama Item', 62, 'L'), array('Batch No', 24, 'C'), array('Unit', 14, 'C'), array('Qty', 10, 'R'), array('Harga', 18, 'R'), array('Total', 30, 'R'), ); if ($pdf->GetY() > 270) { $pdf->AddPage(); } $spanSum = array_sum(array_column(array_slice($cols, 0, 5), 1)); $pdf->SetLineWidth(0.3); $pdf->SetFont('Arial_Narrow', 'B', 8); $pdf->SetFillColor(235, 240, 248); $pdf->SetDrawColor(0, 0, 0); $pdf->Cell($spanSum, 6, 'Sub Total', 1, 0, 'R', true); $pdf->Cell($cols[5][1], 6, $this->_fmt_qty($qty), 1, 0, 'R', true); $pdf->Cell($cols[6][1], 6, '', 1, 0, 'R', true); $pdf->Cell($cols[7][1], 6, $this->_fmt_rp($val), 1, 0, 'R', true); $pdf->Ln(); $pdf->Ln(2); } // ========================================================================= // PDF SECTION: Syarat & Ketentuan // ========================================================================= private function _pdf_terms() { $pdf = $this->_pdf; $pageW = $this->_pageW; $terms = array( 'Laporan ini menampilkan seluruh pemakaian item yang telah dikonfirmasi pada periode yang dipilih.', 'Data pemakaian dikelompokkan berdasarkan divisi yang melakukan pemakaian.', 'Harga yang ditampilkan adalah harga rata-rata pada saat pemakaian item.', 'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan monitoring pemakaian item.', ); $pdf->SetFont('Arial_Narrow', 'B', 9); $pdf->Cell($pageW, 5, 'Syarat & Ketentuan:', 0, 1, 'L'); $pdf->SetFont('Arial_Narrow', '', 8); foreach ($terms as $i => $t) { $pdf->Cell(6, 5, ($i + 1) . '.', 0, 0, 'R'); $pdf->Cell($pageW - 6, 5, $t, 0, 1, 'L'); } } // ========================================================================= // HELPER: Format tampilan // ========================================================================= private function _fmt_date($d) { if (!$d || $d === '0000-00-00') return '-'; return date('d-m-Y', strtotime($d)); } private function _fmt_datetime($d) { if (!$d || $d === '0000-00-00 00:00:00') return '-'; return date('d-m-Y H:i', strtotime($d)); } private function _fmt_num($n) { return number_format(floatval($n), 2, ',', '.'); } private function _fmt_qty($n) { return number_format(floatval($n), 0, ',', '.'); } private function _fmt_rp($n) { return 'Rp ' . number_format(floatval($n), 2, ',', '.'); } }