Files
be-accone/application/controllers/report/Rpt_pemakaian_item_divisi.php
2026-07-17 16:54:03 +07:00

532 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
defined('BASEPATH') or exit('No direct script access allowed');
require_once(APPPATH . 'libraries/fpdf/fpdf.php');
// =============================================================================
// Custom FPDF: override Footer() agar tampil otomatis di SETIAP halaman
// =============================================================================
class PemakaianItemDivisiFpdf extends FPDF
{
public $printUsername = '-';
public $printDate = '';
public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
{
parent::__construct($orientation, $unit, $size);
// Daftarkan Arial Narrow
$this->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, ',', '.');
}
}