2 Commits

Author SHA1 Message Date
Hanan Askarim
0823867277 fix report fpdf 2026-07-17 15:39:07 +07:00
Hanan Askarim
7d8dd0f660 add report payment voucher 2026-07-17 10:03:45 +07:00
18 changed files with 1165 additions and 2667 deletions

View File

@@ -1,532 +0,0 @@
<?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 FakturFpdf 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_faktur extends MY_Controller
{
// -- Properti bersama antar fungsi PDF -------------------------------------
/** @var FakturFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
private $_username;
public function __construct()
{
parent::__construct();
}
public function index()
{
echo "Faktur (Supplier Invoice) Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (SupplierInvoiceID), username (opsional)
// =========================================================================
public function pdf()
{
try {
$id = intval($this->input->get_post('id'));
$username = trim($this->input->get_post('username') ?? '');
if ($id <= 0) {
$this->sys_error("ID tidak valid");
exit;
}
// -- Ambil data header & detail dari DB ----------------------------
$header = $this->_get_header($id);
$details = $this->_get_detail($id);
// -- Inisialisasi FPDF custom --------------------------------------
$this->_pdf = new FakturFpdf('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->_header_data = $header;
$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($details);
$this->_pdf_summary();
$this->_pdf_terms();
// -- Output --------------------------------------------------------
$filename = 'INV_' . str_replace('/', '-', $header['SupplierInvoiceNumber']) . '.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 header
// =========================================================================
private function _get_header($id)
{
$sql = "
SELECT
si.*,
sup.SupplierName,
sup.SupplierAddress,
sup.SupplierPhone,
IFNULL(uCr.M_UserUsername, '') AS CreatedByName,
IFNULL(uVe.M_UserUsername, '') AS VerifiedByName,
IFNULL(uAp.M_UserUsername, '') AS ApprovedByName,
IFNULL(uPa.M_UserUsername, '') AS PaidByName,
IFNULL(uRe.M_UserUsername, '') AS ReceivedByName
FROM supplier_invoice si
LEFT JOIN supplier sup ON sup.SupplierID = si.SupplierInvoiceSupplierID
AND sup.SupplierIsActive = 'Y'
LEFT JOIN m_user uCr ON uCr.M_UserID = si.SupplierInvoiceCreatedUserID
LEFT JOIN m_user uVe ON uVe.M_UserID = si.SupplierInvoiceVerifiedUserID
LEFT JOIN m_user uAp ON uAp.M_UserID = si.SupplierInvoiceApprovedUserID
LEFT JOIN m_user uPa ON uPa.M_UserID = si.SupplierInvoicePaidUserID
LEFT JOIN m_user uRe ON uRe.M_UserID = si.SupplierInvoiceReceivedBy
WHERE si.SupplierInvoiceID = ?
AND si.SupplierInvoiceIsActive = 'Y'
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Faktur tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
sid.*,
i.M_ItemCode,
i.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode
FROM supplier_invoice_detail sid
LEFT JOIN m_item i ON i.M_ItemID = sid.SupplierInvoiceDetailItemID
LEFT JOIN itemunit iu ON iu.ItemUnitID = sid.SupplierInvoiceDetailItemUnitID
WHERE sid.SupplierInvoiceDetailSupplierInvoiceID = ?
AND sid.SupplierInvoiceDetailIsActive = 'Y'
ORDER BY sid.SupplierInvoiceDetailID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
}
// =========================================================================
// PDF SECTION: Header — Judul + Informasi Dokumen (2 kolom)
// =========================================================================
private function _pdf_header()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// -- Judul utama --------------------------------------------------------
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'FAKTUR (SUPPLIER INVOICE)', 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 = 32;
$valW = $halfW - $lblW - 4;
// -- Kolom Kiri -----------------------------------------------------------
$pdf->SetY($startY);
$leftItems = array(
array('Nomor Faktur', $header['SupplierInvoiceNumber'], true),
array('Tgl Faktur', $this->_fmt_date($header['SupplierInvoiceDate']), false),
array('Tgl Jatuh Tempo', $this->_fmt_date($header['SupplierInvoiceDueDate']), false),
array('Status', $header['SupplierInvoiceStatus'], false),
array('Supplier', $header['SupplierName'] ?: '-', false),
);
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');
}
// Alamat Supplier (MultiCell)
if (!empty($header['SupplierAddress'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Alamat Supplier', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->MultiCell($valW, 4, $header['SupplierAddress'], 0, 'L');
$pdf->SetFont('Arial_Narrow', '', 9);
}
// Keterangan
if (!empty($header['SupplierInvoiceNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->MultiCell($valW, 4, $header['SupplierInvoiceNote'], 0, 'L');
$pdf->SetFont('Arial_Narrow', '', 9);
}
$leftY = $pdf->GetY();
// -- Kolom Kanan ----------------------------------------------------------
$pdf->SetY($startY);
$rightX = 15 + $halfW;
// No. Supplier Invoice & tgl (jika ada)
$rightItems = array();
if (!empty($header['SupplierInvoiceSupplierInvoiceNumber'])) {
$rightItems[] = array('No. Inv. Supplier', $header['SupplierInvoiceSupplierInvoiceNumber']);
$rightItems[] = array('Tgl Inv. Supplier', $this->_fmt_date($header['SupplierInvoiceSupplierInvoiceDate']));
}
$rightItems[] = array('No. Delivery Order', $header['SupplierInvoiceDeliveryOrderNumber'] ?: '-');
$rightItems[] = array('Dibuat Oleh', $header['CreatedByName'] ?: '-');
$rightItems[] = array('Dibuat Tgl', $this->_fmt_datetime($header['SupplierInvoiceCreated']));
if ($header['SupplierInvoiceStatus'] === 'Verified' || in_array($header['SupplierInvoiceStatus'], ['Approved', 'Scheduled', 'Paid', 'Partially Paid'])) {
if (!empty($header['SupplierInvoiceVerifiedDate'])) {
$rightItems[] = array('Diverifikasi Oleh', $header['VerifiedByName'] ?: '-');
$rightItems[] = array('Tgl Verifikasi', $this->_fmt_datetime($header['SupplierInvoiceVerifiedDate']));
}
}
if (in_array($header['SupplierInvoiceStatus'], ['Approved', 'Scheduled', 'Paid', 'Partially Paid'])) {
if (!empty($header['SupplierInvoiceApprovedDate'])) {
$rightItems[] = array('Disetujui Oleh', $header['ApprovedByName'] ?: '-');
$rightItems[] = array('Tgl Disetujui', $this->_fmt_datetime($header['SupplierInvoiceApprovedDate']));
}
}
if ($header['SupplierInvoiceStatus'] === 'Paid' || $header['SupplierInvoiceStatus'] === 'Partially Paid') {
if (!empty($header['SupplierInvoicePaidDate'])) {
$rightItems[] = array('Dibayar Oleh', $header['PaidByName'] ?: '-');
$rightItems[] = array('Tgl Dibayar', $this->_fmt_datetime($header['SupplierInvoicePaidDate']));
}
}
// Tgl Terima & Penerima (jika ada)
if (!empty($header['SupplierInvoiceReceiveDate'])) {
$rightItems[] = array('Tgl Terima', $this->_fmt_date($header['SupplierInvoiceReceiveDate']));
$rightItems[] = array('Diterima Oleh', $header['ReceivedByName'] ?: '-');
}
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 detail item
// =========================================================================
private function _pdf_data($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
// -- Definisi kolom: [label, lebar, align] -------------------------------
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 62 + 15 + 20 + 25 + 25 + 25 = 180
$cols = array(
array('No', 8, 'C'),
array('Deskripsi', 62, 'L'),
array('Unit', 15, 'C'),
array('Qty', 20, 'R'),
array('Harga Satuan', 25, 'R'),
array('Diskon', 25, 'R'),
array('Total', 25, 'R'),
);
// 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], 7, $c[0], 1, 0, 'C', true);
}
$pdf->Ln();
// Baris data
$pdf->SetLineWidth(0.2);
$pdf->SetFont('Arial_Narrow', '', 7.5);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
foreach ($details as $d) {
$qty = floatval($d['SupplierInvoiceDetailQty'] ?? 0);
$price = floatval($d['SupplierInvoiceDetailPrice'] ?? 0);
$discAmount = floatval($d['SupplierInvoiceDetailDiscountAmount'] ?? 0);
$total = floatval($d['SupplierInvoiceDetailTotal'] ?? 0);
$desc = $d['SupplierInvoiceDetailDescription'] ?: ($d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'));
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $desc, 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'),1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($qty), 1, 0, 'R');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($price), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $discAmount > 0 ? $this->_fmt_rp($discAmount) : '-', 1, 0, 'R');
$pdf->Cell($cols[6][1], 6, $this->_fmt_rp($total), 1, 0, 'R');
$pdf->Ln();
$no++;
}
$pdf->Ln(4);
}
// =========================================================================
// PDF SECTION: Ringkasan Nilai (SubTotal, Pajak, Grand Total)
// =========================================================================
private function _pdf_summary()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// -- Ringkasan nilai (rata kanan) ----------------------------------------
$cW1 = 50;
$cW2 = 40;
$offsetX = 15 + $this->_pageW - $cW1 - $cW2;
// Garis pemisah
$pdf->SetDrawColor(0, 0, 0);
$pdf->SetLineWidth(0.3);
$pdf->Line(15 + $pageW - $cW1 - $cW2 - 4, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(2);
$items = array();
// Sub Total
$items[] = array('Sub Total', $this->_fmt_rp($header['SupplierInvoiceSubTotal']));
// Diskon (jika ada)
$discPct = floatval($header['SupplierInvoiceDiscountPercent'] ?? 0);
$discAmt = floatval($header['SupplierInvoiceDiscountAmount'] ?? 0);
if ($discPct > 0 || $discAmt > 0) {
$label = 'Diskon';
if ($discPct > 0) $label .= ' (' . $this->_fmt_num($discPct) . '%)';
$items[] = array($label, $this->_fmt_rp($discAmt));
}
// PPh (jika ada)
$pphPct = floatval($header['SupplierInvoiceTaxPercentPph'] ?? 0);
$pphAmt = floatval($header['SupplierInvoiceTaxAmountPph'] ?? 0);
if ($pphPct > 0 || $pphAmt > 0) {
$label = 'PPh';
if ($pphPct > 0) $label .= ' (' . $this->_fmt_num($pphPct) . '%)';
$items[] = array($label, $this->_fmt_rp($pphAmt));
}
// PPN (jika ada)
$ppnPct = floatval($header['SupplierInvoiceTaxPercentPpn'] ?? 0);
$ppnAmt = floatval($header['SupplierInvoiceTaxAmountPpn'] ?? 0);
if ($ppnPct > 0 || $ppnAmt > 0) {
$label = 'PPN';
if ($ppnPct > 0) $label .= ' (' . $this->_fmt_num($ppnPct) . '%)';
$items[] = array($label, $this->_fmt_rp($ppnAmt));
}
// Biaya Kirim (jika ada)
$shipCost = floatval($header['SupplierInvoiceShippingCost'] ?? 0);
if ($shipCost > 0) {
$items[] = array('Biaya Kirim', $this->_fmt_rp($shipCost));
}
// Penyesuaian (jika ada)
$adjAmt = floatval($header['SupplierInvoiceAdjustmentAmount'] ?? 0);
if ($adjAmt != 0) {
$label = 'Penyesuaian';
if (!empty($header['SupplierInvoiceAdjustmentNote'])) {
$label .= ' (' . $header['SupplierInvoiceAdjustmentNote'] . ')';
}
$items[] = array($label, $this->_fmt_rp($adjAmt));
}
foreach ($items as $item) {
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->Cell($cW1, 5, $item[0], 0, 0, 'L');
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->Cell($cW2, 5, $item[1], 0, 1, 'R');
}
// Grand Total (tebal)
$pdf->SetX($offsetX);
$pdf->SetDrawColor(0, 0, 0);
$pdf->SetLineWidth(0.3);
$pdf->Line($offsetX, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(1);
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', 'B', 10);
$pdf->Cell($cW1, 6, 'Grand Total', 0, 0, 'L');
$pdf->Cell($cW2, 6, $this->_fmt_rp($header['SupplierInvoiceGrandTotal']), 0, 1, 'R');
// Sudah dibayar / sisa
$paidAmt = floatval($header['SupplierInvoicePaidAmount'] ?? 0);
$unpaidAmt = floatval($header['SupplierInvoiceUnpaid'] ?? 0);
$isLunas = $header['SupplierInvoiceIsLunas'] ?? 'N';
if ($paidAmt > 0) {
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->Cell($cW1, 5, 'Dibayar', 0, 0, 'L');
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->Cell($cW2, 5, $this->_fmt_rp($paidAmt), 0, 1, 'R');
}
if ($isLunas === 'Y') {
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', 'B', 9);
$pdf->SetTextColor(0, 128, 0);
$pdf->Cell($cW1 + $cW2, 5, 'LUNAS', 0, 1, 'R');
$pdf->SetTextColor(0, 0, 0);
} elseif ($unpaidAmt > 0) {
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->Cell($cW1, 5, 'Sisa Tagihan', 0, 0, 'L');
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->Cell($cW2, 5, $this->_fmt_rp($unpaidAmt), 0, 1, 'R');
}
$pdf->Ln(4);
}
// =========================================================================
// PDF SECTION: Syarat & Ketentuan
// =========================================================================
private function _pdf_terms()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$terms = array(
'Faktur ini diterbitkan berdasarkan barang/jasa yang telah diterima dan sesuai dengan purchase order.',
'Pembayaran harus dilakukan sesuai dengan tanggal jatuh tempo yang tercantum.',
'Keterlambatan pembayaran akan dikenakan sanksi sesuai ketentuan yang berlaku.',
'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan administrasi perusahaan.',
);
$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, ',', '.');
}
}

View File

@@ -6,7 +6,7 @@ require_once(APPPATH . 'libraries/fpdf/fpdf.php');
// =============================================================================
// Custom FPDF: override Footer() agar tampil otomatis di SETIAP halaman
// =============================================================================
class RequestMutasiFpdf extends FPDF
class PaymentVoucherFpdf extends FPDF
{
public $printUsername = '-';
public $printDate = '';
@@ -14,14 +14,14 @@ class RequestMutasiFpdf extends FPDF
public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
{
parent::__construct($orientation, $unit, $size);
// Daftarkan Arial Narrow
// Daftarkan Arial Narrow — file font ada di fpdf/font/Arial_Narrow.php
$this->AddFont('Arial_Narrow', '', 'Arial_Narrow.php'); // regular
$this->AddFont('Arial_Narrow', 'B', 'Arial_Narrow_B.php'); // bold
$this->AddFont('Arial_Narrow', 'B', 'Arial_Narrow_B.php'); // bold (Liberation Sans Narrow Bold)
}
public function Footer()
{
$pageW = $this->GetPageWidth() - 30; // margin 15+15
$pageW = $this->GetPageWidth() - 30; // sama dengan margin 15+15
// ── Posisi: 20mm dari bawah halaman ──────────────────────────────────
$this->SetY(-20);
@@ -40,15 +40,119 @@ class RequestMutasiFpdf extends FPDF
$this->Cell($colSide, 4, 'Tgl Print : ' . $this->printDate, 0, 0, 'L');
$this->Cell($colCenter + $colSide, 4, '', 0, 1, 'L');
}
// Properties untuk tabel multiline
public $widths;
public $aligns;
public function SetWidths($w)
{
$this->widths = $w;
}
public function SetAligns($a)
{
$this->aligns = $a;
}
public function Row($data, $fill = false, $h = 6)
{
// Hitung tinggi maksimum baris berdasarkan multiline
$nb = 0;
for ($i = 0; $i < count($data); $i++) {
$nb = max($nb, $this->NbLines($this->widths[$i], $data[$i]));
}
$rowH = $h * $nb;
// Cek apakah perlu ganti halaman secara otomatis
$this->CheckPageBreak($rowH);
// Gambar cell pada baris
for ($i = 0; $i < count($data); $i++) {
$w = $this->widths[$i];
$a = isset($this->aligns[$i]) ? $this->aligns[$i] : 'L';
$x = $this->GetX();
$y = $this->GetY();
// Gambar border dan background
$this->Rect($x, $y, $w, $rowH, $fill ? 'DF' : 'D');
// Tulis teks menggunakan MultiCell
$this->MultiCell($w, $h, $data[$i], 0, $a);
// Geser posisi X ke kanan untuk cell berikutnya
$this->SetXY($x + $w, $y);
}
// Pindah baris
$this->Ln($rowH);
}
public function CheckPageBreak($h)
{
// Jika tinggi baris melewati batas, buat halaman baru
if ($this->GetY() + $h > $this->PageBreakTrigger) {
$this->AddPage($this->CurOrientation);
}
}
public function NbLines($w, $txt)
{
// Menghitung jumlah baris yang akan dihasilkan oleh MultiCell
$cw =& $this->CurrentFont['cw'];
if ($w == 0) {
$w = $this->w - $this->rMargin - $this->x;
}
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
$s = str_replace("\r", '', $txt);
$nb = strlen($s);
if ($nb > 0 && $s[$nb - 1] == "\n") {
$nb--;
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while ($i < $nb) {
$c = $s[$i];
if ($c == "\n") {
$i++;
$sep = -1;
$j = $i;
$l = 0;
$nl++;
continue;
}
if ($c == ' ') {
$sep = $i;
}
$l += $cw[$c];
if ($l > $wmax) {
if ($sep == -1) {
if ($i == $j) {
$i++;
}
} else {
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
$nl++;
} else {
$i++;
}
}
return $nl;
}
}
// =============================================================================
// Controller
// =============================================================================
class Rpt_request_mutasi extends MY_Controller
class Rpt_payment_voucher extends MY_Controller
{
// ── Properti bersama antar fungsi PDF ─────────────────────────────────────
/** @var RequestMutasiFpdf */
/** @var PaymentVoucherFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
@@ -61,12 +165,12 @@ class Rpt_request_mutasi extends MY_Controller
public function index()
{
echo "Request Mutasi Report API";
echo "Payment Voucher Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (MutasiRequestID), username (opsional)
// GET/POST: id (PaymentVoucherID), username (opsional)
// =========================================================================
public function pdf()
{
@@ -84,7 +188,7 @@ class Rpt_request_mutasi extends MY_Controller
$details = $this->_get_detail($id);
// ── Inisialisasi FPDF custom ──────────────────────────────────────
$this->_pdf = new RequestMutasiFpdf('P', 'mm', 'A4');
$this->_pdf = new PaymentVoucherFpdf('P', 'mm', 'A4');
$this->_pdf->printUsername = $username !== '' ? $username : '-';
$this->_pdf->printDate = date('d-m-Y H:i:s');
$this->_pageW = $this->_pdf->GetPageWidth() - 30;
@@ -102,7 +206,7 @@ class Rpt_request_mutasi extends MY_Controller
$this->_pdf_terms();
// ── Output ────────────────────────────────────────────────────────
$filename = 'RM_' . str_replace('/', '-', $header['MutasiRequestNumber']) . '.pdf';
$filename = 'PV_' . str_replace('/', '-', $header['PaymentVoucherNumber']) . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Cache-Control: private, max-age=0, must-revalidate');
@@ -120,54 +224,55 @@ class Rpt_request_mutasi extends MY_Controller
{
$sql = "
SELECT
mr.*,
ic.itemCategoryID,
ic.itemCategoryName,
a.M_BranchID AS branch_asal_id,
a.M_BranchCode AS branch_asal_code,
a.M_BranchName AS branch_asal_name,
b.M_BranchID AS branch_tujuan_id,
b.M_BranchCode AS branch_tujuan_code,
b.M_BranchName AS branch_tujuan_name,
IFNULL(u.M_UserUsername, '') AS CreatedByName,
IFNULL(uv.M_UserUsername, '') AS VerifByName,
IFNULL(ua.M_UserUsername, '') AS ApprovedByName
FROM mutasi_request mr
JOIN item_category ic ON ic.itemCategoryID = mr.MutasiRequestItemCategoryID
JOIN m_branch a ON a.M_BranchID = mr.MutasiRequestFromBranchID
JOIN m_branch b ON b.M_BranchID = mr.MutasiRequestToBranchID
LEFT JOIN m_user u ON u.M_UserID = mr.MutasiRequestUserID
LEFT JOIN m_user uv ON uv.M_UserID = mr.MutasiRequestVerifUserID
LEFT JOIN m_user ua ON ua.M_UserID = mr.MutasiRequestApprovedUserID
WHERE mr.MutasiRequestID = ?
pv.*,
cs.coaAccountNo,
cs.coaDescription,
ct.coaAccountNo AS coaTempAccountNo,
ct.coaDescription AS coaTempDescription,
b.M_BranchName,
IFNULL(uCr.M_UserUsername, '') AS CreatedByName,
IFNULL(uPd.M_UserUsername, '') AS PaidByName,
IFNULL(uRc.M_UserUsername, '') AS PaidReceiveByName
FROM payment_voucher pv
LEFT JOIN coa cs ON cs.coaID = pv.PaymentVoucherCoaSourceID
LEFT JOIN coa ct ON ct.coaID = pv.PaymentVoucherCoaTemporaryID
LEFT JOIN m_branch b ON b.M_BranchCode = pv.PaymentVoucherM_BranchCode
LEFT JOIN m_user uCr ON uCr.M_UserID = pv.PaymentVoucherUserID
LEFT JOIN m_user uPd ON uPd.M_UserID = pv.PaymentVoucherPaidUserID
LEFT JOIN m_user uRc ON uRc.M_UserID = pv.PaymentVoucherPaidReceiveUserID
WHERE pv.PaymentVoucherIsActive = 'Y'
AND pv.PaymentVoucherID = ?
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Request Mutasi tidak ditemukan");
$this->sys_error("Data Payment Voucher tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
mrd.*,
i.M_ItemCode,
i.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode
FROM mutasi_request_detail mrd
LEFT JOIN m_item i ON i.M_ItemID = mrd.MutasiRequestDetailM_ItemID
LEFT JOIN itemunit iu ON iu.ItemUnitID = mrd.MutasiRequestDetailItemUnitID
WHERE mrd.MutasiRequestDetailMutasiRequestID = ?
AND mrd.MutasiRequestDetailIsActive = 'Y'
ORDER BY mrd.MutasiRequestDetailID ASC
pvd.*,
prd.PurchaseRequestDirectNumber,
prdd.PurchaseRequestDirectDescription AS ItemDescription,
prdd.PurchaseRequestDirectDetailAccount AS ExpenseAccount,
prdd.PurchaseRequestDirectDetailTotalRealitationPrice,
prdd.PurchaseRequestDirectDetailTotalEstimationPrice,
pdc.PurchaseDirectCategoryName,
c.coaDescription AS ExpenseAccountName
FROM payment_voucher_detail pvd
JOIN purchase_request_direct prd ON prd.PurchaseRequestDirectID = pvd.PaymentVoucherDetailPurchaseRequestDirectID
JOIN purchase_request_direct_detail prdd ON prdd.PurchaseRequestDirectDetailPurchaseRequestDirectID = prd.PurchaseRequestDirectID
AND prdd.PurchaseRequestDirectDetailIsActive = 'Y'
LEFT JOIN purchase_direct_category pdc ON pdc.PurchaseDirectCategoryID = prdd.PurchaseRequestDirectDetailPurchaseRequestDirectCategoryID
LEFT JOIN coa c ON c.coaAccountNo = prdd.PurchaseRequestDirectDetailAccount AND c.coaIsActive = 'Y'
WHERE pvd.PaymentVoucherDetailIsActive = 'Y'
AND pvd.PaymentVoucherDetailPaymentVoucherID = ?
ORDER BY pvd.PaymentVoucherDetailID ASC, prdd.PurchaseRequestDirectDetailID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
@@ -184,7 +289,7 @@ class Rpt_request_mutasi extends MY_Controller
// ── Judul utama ───────────────────────────────────────────────────────
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'REQUEST MUTASI REPORT (RM)', 0, 1, 'L');
$pdf->Cell($pageW, 8, 'PAYMENT VOUCHER (BUKTI PENGELUARAN KAS / BANK)', 0, 1, 'L');
$pdf->SetDrawColor(0, 0, 0);
$pdf->SetLineWidth(0.5);
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
@@ -192,39 +297,62 @@ class Rpt_request_mutasi extends MY_Controller
$startY = $pdf->GetY();
$halfW = $pageW / 2;
$lblW = 30;
$lblW = 28;
$valW = $halfW - $lblW - 4;
// ── Kolom Kiri ────────────────────────────────────────────────────────
$pdf->SetY($startY);
$coaSource = '-';
if (!empty($header['coaAccountNo'])) {
$coaSource = $header['coaAccountNo'] . ' - ' . $header['coaDescription'];
}
$coaTemp = '';
if (!empty($header['coaTempAccountNo'])) {
$coaTemp = $header['coaTempAccountNo'] . ' - ' . $header['coaTempDescription'];
}
$leftItems = array(
array('Nomor', $header['MutasiRequestNumber'], true),
array('Tanggal', $this->_fmt_date($header['MutasiRequestDate']), false),
array('Status', $header['MutasiRequestStatus'], false),
array('Kategori', $header['itemCategoryName'], false),
array('Nomor', $header['PaymentVoucherNumber'], true),
array('Tanggal', $this->_fmt_date($header['PaymentVoucherDate']), false),
array('Sumber Dana', $coaSource, false),
);
if ($coaTemp !== '') {
$leftItems[] = array('Akun Transit', $coaTemp, false);
}
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');
$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);
}
if ($item[0] === 'Sumber Dana' || $item[0] === 'Akun Transit') {
$pdf->MultiCell($valW, 5, $item[1], 0, 'L');
} else {
$pdf->Cell($valW, 5, $item[1], 0, 1, 'L');
}
}
// Keterangan (Note) langsung di bawah Status
if (!empty($header['MutasiRequestNote'])) {
// Keterangan / Paid Note langsung di bawah Status (tanpa spasi vertikal)
if (!empty($header['PaymentVoucherPaidNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell($lblW, 5, 'Catatan Bayar', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $header['MutasiRequestNote'], 0, 'L');
$pdf->MultiCell($valW, 5, $header['PaymentVoucherPaidNote'], 0, 'L');
}
$leftY = $pdf->GetY();
@@ -233,11 +361,9 @@ class Rpt_request_mutasi extends MY_Controller
$rightX = 15 + $halfW;
$rightItems = array(
array('Cabang Asal', $header['branch_asal_name']),
array('Cabang Tujuan', $header['branch_tujuan_name']),
array('Dibuat Oleh', $header['CreatedByName']),
array('Diverifikasi Oleh', $header['VerifByName'] ?: '-'),
array('Disetujui Oleh', $header['ApprovedByName'] ?: '-'),
array('Cabang', $header['M_BranchName'] ?: '-'),
array('Tgl Pembayaran', $this->_fmt_date($header['PaymentVoucherPaidDate'])),
array('Dibuat Oleh', $header['CreatedByName'] ?: '-'),
);
foreach ($rightItems as $item) {
@@ -248,22 +374,6 @@ class Rpt_request_mutasi extends MY_Controller
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $item[1], 0, 1, 'L');
}
// Tanggal approvals di kolom kanan
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Tgl Verifikasi', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $this->_fmt_datetime($header['MutasiRequestVerifDate']), 0, 1, 'L');
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Tgl Approved', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $this->_fmt_datetime($header['MutasiRequestApprovedDate']), 0, 1, 'L');
$rightY = $pdf->GetY();
// Posisikan Y ke yang paling bawah + margin
@@ -271,7 +381,7 @@ class Rpt_request_mutasi extends MY_Controller
}
// =========================================================================
// PDF SECTION: Data — Tabel detail item + ringkasan nilai
// PDF SECTION: Data — Tabel detail item
// =========================================================================
private function _pdf_data($details)
{
@@ -281,18 +391,17 @@ class Rpt_request_mutasi extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 80 + 20 + 20 + 25 + 27 = 180
// 8 + 45 + 30 + 72 + 25 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama Item', 80, 'L'),
array('Unit', 20, 'C'),
array('Qty', 20, 'R'),
array('Nilai Buku', 25, 'R'),
array('Total', 27, 'R'),
array('No', 8, 'C'),
array('No. Request Direct', 45, 'L'),
array('Kategori', 30, 'L'),
array('Deskripsi', 72, 'L'),
array('Nominal Bayar', 25, 'R'),
);
// Header kolom
$pdf->SetLineWidth(0.3);
$pdf->SetLineWidth(0.3); // border medium
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->SetDrawColor(0, 0, 0);
@@ -301,37 +410,48 @@ class Rpt_request_mutasi extends MY_Controller
}
$pdf->Ln();
// Set lebar dan alignment kolom untuk metode Row()
$pdf->SetWidths(array_column($cols, 1));
$pdf->SetAligns(array_column($cols, 2));
// Baris data
$pdf->SetLineWidth(0.2);
$pdf->SetLineWidth(0.2); // border lebih tipis di data
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
$total_val = 0;
$totalPay = 0;
foreach ($details as $d) {
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($d['MutasiRequestDetailQty']), 1, 0, 'R');
$bookValue = floatval($d['MutasiRequestDetailBookValue'] ?? 0);
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($bookValue), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['MutasiRequestDetailQty'] * $bookValue), 1, 0, 'R');
$pdf->Ln();
$amount = floatval($d['PurchaseRequestDirectDetailTotalRealitationPrice']);
if ($amount <= 0) {
$amount = floatval($d['PurchaseRequestDirectDetailTotalEstimationPrice']);
}
$total_val += floatval($d['MutasiRequestDetailQty'] * $bookValue);
$row_data = array(
$no,
$d['PurchaseRequestDirectNumber'] ?: '-',
$d['PurchaseDirectCategoryName'] ?: '-',
$d['ItemDescription'] ?: '-',
$this->_fmt_rp($amount),
);
$pdf->Row($row_data);
$totalPay += $amount;
$no++;
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 5), 1));
$span = array_sum(array_column(array_slice($cols, 0, 4), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 9);
$pdf->SetFont('Arial_Narrow', 'B', 9); // bold + size 9 agar menonjol
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[5][1], 7, $this->_fmt_rp($total_val), 1, 0, 'R', true);
$pdf->Ln();
$pdf->Cell($span, 7, 'TOTAL PEMBAYARAN', 1, 0, 'R', true);
$pdf->Cell($cols[4][1], 7, $this->_fmt_rp($totalPay), 1, 0, 'R', true);
$pdf->Ln(6);
$pdf->Ln(4);
// Ringkasan Biaya di Kanan Bawah (sejajar dengan Nominal Bayar)
$pdf->Ln(2);
}
// =========================================================================
@@ -343,10 +463,10 @@ class Rpt_request_mutasi extends MY_Controller
$pageW = $this->_pageW;
$terms = array(
'Mutasi barang ini harus mendapatkan persetujuan dari pejabat berwenang sebelum proses mutasi dilakukan.',
'Barang yang dimutasi harus sesuai dengan spesifikasi dan jumlah yang tercantum dalam dokumen ini.',
'Proses mutasi dilakukan setelah barang diverifikasi dan disetujui oleh pihak terkait.',
'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan proses mutasi barang.',
'Voucher ini adalah tanda bukti pengeluaran kas/bank yang sah untuk pembayaran dokumen PR Direct di atas.',
'Penerima dana bertanggung jawab penuh atas keabsahan penggunaan dana yang diserahterimakan.',
'Realisasi pembayaran harus dilaporkan kembali ke bagian keuangan selambat-lambatnya 3 hari kerja.',
'Dokumen ini dicetak secara digital dan divalidasi oleh otorisasi sistem akuntansi ERP.'
);
$pdf->SetFont('Arial_Narrow', 'B', 9);
@@ -379,6 +499,7 @@ class Rpt_request_mutasi extends MY_Controller
return number_format(floatval($n), 2, ',', '.');
}
// format jumlah/qty: tanpa desimal
private function _fmt_qty($n)
{
return number_format(floatval($n), 0, ',', '.');

View File

@@ -1,531 +0,0 @@
<?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, ',', '.');
}
}

View File

@@ -1,427 +0,0 @@
<?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 MutasiPenerimaanFpdf 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_penerimaan_mutasi extends MY_Controller
{
// ── Properti bersama antar fungsi PDF ─────────────────────────────────────
/** @var MutasiPenerimaanFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
private $_username;
public function __construct()
{
parent::__construct();
}
public function index()
{
echo "Mutasi Penerimaan (Receive Mutasi) Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (MutasiHandoverID), username (opsional)
// =========================================================================
public function pdf()
{
try {
$id = intval($this->input->get_post('id'));
$username = trim($this->input->get_post('username') ?? '');
if ($id <= 0) {
$this->sys_error("ID tidak valid");
exit;
}
// ── Ambil data header & detail dari DB ────────────────────────────
$header = $this->_get_header($id);
// Validasi: hanya dokumen berstatus Received yang bisa dicetak
if ($header['MutasiHandoverStatus'] !== 'Received') {
$this->sys_error("Dokumen belum diterima (Status: " . $header['MutasiHandoverStatus'] . ")");
exit;
}
$details = $this->_get_detail($id);
// ── Inisialisasi FPDF custom ──────────────────────────────────────
$this->_pdf = new MutasiPenerimaanFpdf('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->_header_data = $header;
$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($details);
$this->_pdf_terms();
// ── Output ────────────────────────────────────────────────────────
$filename = 'PM_' . str_replace('/', '-', $header['MutasiHandoverNumber']) . '.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 header
// =========================================================================
private function _get_header($id)
{
$sql = "
SELECT
mh.*,
mr.MutasiRequestNumber,
ic.itemCategoryID,
ic.itemCategoryName,
a.M_BranchID AS branch_asal_id,
a.M_BranchCode AS branch_asal_code,
a.M_BranchName AS branch_asal_name,
b.M_BranchID AS branch_tujuan_id,
b.M_BranchCode AS branch_tujuan_code,
b.M_BranchName AS branch_tujuan_name,
IFNULL(u.M_UserUsername, '') AS CreatedByName,
IFNULL(ur.M_UserUsername, '') AS ReceiveByName,
CASE
WHEN wh.WarehouseType = 'B' THEN CONCAT(wh.WarehouseCode, ' ', wh.WarehouseName, ' - ', b.M_BranchName)
ELSE wh.WarehouseName
END AS WarehouseName,
r.M_RuanganName
FROM mutasi_handover mh
LEFT JOIN mutasi_request mr ON mr.MutasiRequestID = mh.MutasiHandoverMutasiRequestID
JOIN item_category ic ON ic.itemCategoryID = mh.MutasiHandoverItemCategoryID
AND ic.itemCategoryIsActive = 'Y'
JOIN m_branch a ON a.M_BranchID = mh.MutasiHandoverFromBranchID
AND a.M_BranchIsActive = 'Y'
JOIN m_branch b ON b.M_BranchID = mh.MutasiHandoverToBranchID
AND b.M_BranchIsActive = 'Y'
LEFT JOIN m_user u ON u.M_UserID = mh.MutasiHandoverUserID
LEFT JOIN m_user ur ON ur.M_UserID = mh.MutasiHandoverReceiveUserID
LEFT JOIN warehouse wh ON wh.WarehouseID = mh.MutasiHandoverReceiveWarehouseID
AND wh.WarehouseIsActive = 'Y'
LEFT JOIN m_ruangan r ON r.M_RuanganID = mh.MutasiHandoverReceiveM_RuanganID
AND r.M_RuanganIsActive = 'Y'
WHERE mh.MutasiHandoverID = ?
AND mh.MutasiHandoverIsActive = 'Y'
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Penerimaan Mutasi tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
mhd.*,
i.M_ItemCode,
i.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode,
st.StockStockNumber AS StockToNumber,
sf.StockStockNumber AS StockFromNumber
FROM mutasi_handover_detail mhd
LEFT JOIN m_item i ON i.M_ItemID = mhd.MutasiHandoverDetailM_ItemID
LEFT JOIN itemunit iu ON iu.ItemUnitID = mhd.MutasiHandoverDetailItemUnitID
LEFT JOIN stock sf ON sf.StockID = mhd.MutasiHandoverDetailStockID_From
LEFT JOIN stock st ON st.StockID = mhd.MutasiHandoverDetailStockID_To
WHERE mhd.MutasiHandoverDetailMutasiHandoverID = ?
AND mhd.MutasiHandoverDetailIsActive = 'Y'
ORDER BY mhd.MutasiHandoverDetailID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
}
// =========================================================================
// PDF SECTION: Header — Judul + Informasi Dokumen (2 kolom)
// =========================================================================
private function _pdf_header()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// ── Judul utama ───────────────────────────────────────────────────────
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'PENERIMAAN MUTASI', 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('Nomor Handover', $header['MutasiHandoverNumber'], true),
array('No. Request', $header['MutasiRequestNumber'] ?: '-', false),
array('Tgl Kirim', $this->_fmt_date($header['MutasiHandoverDate']), false),
array('Tgl Terima', $this->_fmt_date($header['MutasiHandoverReceiveDate']), false),
array('Status', $header['MutasiHandoverStatus'], false),
array('Kategori', $header['itemCategoryName'], false),
);
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');
}
// Keterangan (Note) langsung di bawah Kategori
if (!empty($header['MutasiHandoverNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $header['MutasiHandoverNote'], 0, 'L');
}
$leftY = $pdf->GetY();
// ── Kolom Kanan ───────────────────────────────────────────────────────
$pdf->SetY($startY);
$rightX = 15 + $halfW;
$rightItems = array(
array('Cabang Asal', $header['branch_asal_name']),
array('Cabang Tujuan', $header['branch_tujuan_name']),
array('Warehouse Tujuan', $header['WarehouseName'] ?: '-'),
array('Ruangan Tujuan', $header['M_RuanganName'] ?: '-'),
array('Diterima Oleh', $header['MutasiHandoverStatus'] === 'Received' ? $header['ReceiveByName'] : '-'),
array('Dibuat Oleh', $header['CreatedByName']),
);
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 detail item + ringkasan nilai
// =========================================================================
private function _pdf_data($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 72 + 15 + 15 + 22 + 22 + 26 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama Item', 72, 'L'),
array('Unit', 15, 'C'),
array('Qty', 15, 'R'),
array('Nilai Buku', 22, 'R'),
array('Total', 22, 'R'),
array('Stock Tujuan', 26, 'C'),
);
// 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], 7, $c[0], 1, 0, 'C', true);
}
$pdf->Ln();
// Baris data
$pdf->SetLineWidth(0.2);
$pdf->SetFont('Arial_Narrow', '', 7.5);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
$total_val = 0;
foreach ($details as $d) {
$bookValue = floatval($d['MutasiHandoverDetailBookValue'] ?? 0);
$qty = floatval($d['MutasiHandoverDetailQty'] ?? 0);
$lineTotal = $qty * $bookValue;
$stockTo = $d['StockToNumber'] ?: '-';
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($qty), 1, 0, 'R');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($bookValue), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($lineTotal), 1, 0, 'R');
$pdf->Cell($cols[6][1], 6, $stockTo, 1, 0, 'C');
$pdf->Ln();
$total_val += $lineTotal;
$no++;
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 6), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[6][1], 7, '', 1, 0, 'C', true);
$pdf->Ln();
// ── Ringkasan nilai (rata kanan) ──────────────────────────────────────
$pdf->Ln(3);
$summary = array(
array('Total Nilai Penerimaan', $this->_fmt_rp($header['MutasiHandoverTotalValue'])),
);
$cW1 = 50;
$cW2 = 40;
$offsetX = 15 + $this->_pageW - $cW1 - $cW2;
foreach ($summary as $s) {
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->Cell($cW1, 5, $s[0], 0, 0, 'L');
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->Cell($cW2, 5, $s[1], 0, 1, 'R');
}
$pdf->Ln(4);
}
// =========================================================================
// PDF SECTION: Syarat & Ketentuan
// =========================================================================
private function _pdf_terms()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$terms = array(
'Penerimaan mutasi ini dilakukan berdasarkan dokumen handover (penyerahan) yang telah dikirim oleh cabang asal.',
'Penerima wajib memeriksa kesesuaian jumlah, jenis, dan kondisi barang dengan dokumen handover.',
'Setelah diterima, barang menjadi tanggung jawab cabang tujuan dan akan dicatat dalam stok tujuan.',
'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan proses mutasi barang.',
);
$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, ',', '.');
}
}

View File

@@ -1,403 +0,0 @@
<?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 PengeluaranBarangFpdf 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_pengeluaran_barang extends MY_Controller
{
// -- Properti bersama antar fungsi PDF -------------------------------------
/** @var PengeluaranBarangFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
private $_username;
public function __construct()
{
parent::__construct();
}
public function index()
{
echo "Pengeluaran Barang Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (T_ItemOutID), username (opsional)
// =========================================================================
public function pdf()
{
try {
$id = intval($this->input->get_post('id'));
$username = trim($this->input->get_post('username') ?? '');
if ($id <= 0) {
$this->sys_error("ID tidak valid");
exit;
}
// -- Ambil data header & detail dari DB ----------------------------
$header = $this->_get_header($id);
$details = $this->_get_detail($id);
// -- Inisialisasi FPDF custom --------------------------------------
$this->_pdf = new PengeluaranBarangFpdf('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->_header_data = $header;
$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($details);
$this->_pdf_terms();
// -- Output --------------------------------------------------------
$filename = 'IO_' . str_replace('/', '-', $header['T_ItemOutNumber']) . '.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 header
// =========================================================================
private function _get_header($id)
{
$sql = "
SELECT
io.*,
IFNULL(u.M_UserUsername, '') AS CreatedByName,
IFNULL(ur.M_UserUsername, '') AS ReceiveByName,
IFNULL(ua.M_UserUsername, '') AS ApprovedByName,
wh.WarehouseCode,
wh.WarehouseName,
wh.WarehouseType,
d.DivisionCode,
d.DivisionName
FROM t_item_out io
LEFT JOIN m_user u ON u.M_UserID = io.T_ItemOutUserID
LEFT JOIN m_user ur ON ur.M_UserID = io.T_ItemOutReceiveUserID
LEFT JOIN m_user ua ON ua.M_UserID = io.T_ItemOutApproveID
LEFT JOIN warehouse wh ON wh.WarehouseID = io.T_ItemOutWarehouseID
AND wh.WarehouseIsActive = 'Y'
LEFT JOIN division d ON d.DivisionID = io.T_ItemOutDivisionID
AND d.DivisionIsActive = 'Y'
WHERE io.T_ItemOutID = ?
AND io.T_ItemOutIsActive = 'Y'
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Pengeluaran Barang tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
iod.*,
i.M_ItemCode,
i.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode,
rod.RequestItemOutDetailQty AS RequestQty,
rod.RequestItemOutDetailStatus AS RequestStatus
FROM t_item_out_detail iod
LEFT JOIN m_item i ON i.M_ItemID = iod.T_ItemOutDetailM_ItemID
LEFT JOIN itemunit iu ON iu.ItemUnitID = iod.T_ItemOutDetailItemUnitID
LEFT JOIN request_item_out_detail rod
ON rod.RequestItemOutDetailID = iod.T_ItemOutDetailRequestItemOutDetailID
AND rod.RequestItemOutDetailIsActive = 'Y'
WHERE iod.T_ItemOutDetailT_ItemOutID = ?
AND iod.T_ItemOutDetailIsActive = 'Y'
ORDER BY iod.T_ItemOutDetailID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
}
// =========================================================================
// PDF SECTION: Header — Judul + Informasi Dokumen (2 kolom)
// =========================================================================
private function _pdf_header()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// -- Judul utama -------------------------------------------------------
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'PENGELUARAN BARANG (ITEM OUT)', 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('Nomor IO', $header['T_ItemOutNumber'], true),
array('Tanggal', $this->_fmt_date($header['T_ItemOutDate']), false),
array('Status', $header['T_ItemOutStatus'], false),
array('Confirm', $header['T_ItemOutIsConfirm'] === 'Y' ? 'Ya' : 'Tidak', false),
array('Divisi', $header['DivisionName'] ?: '-', false),
array('Gudang', $header['WarehouseName'] ?: '-', false),
);
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');
}
// Keterangan (Note) langsung di bawah field terakhir
if (!empty($header['T_ItemOutNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $header['T_ItemOutNote'], 0, 'L');
}
$leftY = $pdf->GetY();
// -- Kolom Kanan -------------------------------------------------------
$pdf->SetY($startY);
$rightX = 15 + $halfW;
$rightItems = array(
array('Dibuat Oleh', $header['CreatedByName'] ?: '-'),
array('Dibuat Tgl', $this->_fmt_datetime($header['T_ItemOutCreated'])),
array('Penerima', $header['ReceiveByName'] ?: '-'),
array('Disetujui Oleh', $header['ApprovedByName'] ?: '-'),
array('Tgl Approve', $header['T_ItemOutApproveDate'] ? $this->_fmt_datetime($header['T_ItemOutApproveDate']) : '-'),
array('Tgl Dikonfirmasi', $header['T_ItemOutIsConfirmDate'] ? $this->_fmt_datetime($header['T_ItemOutIsConfirmDate']) : '-'),
);
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 detail item
// =========================================================================
private function _pdf_data($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
// -- Definisi kolom: [label, lebar, align] -----------------------------
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 72 + 20 + 20 + 30 + 30 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama Item', 72, 'L'),
array('Unit', 20, 'C'),
array('Qty', 20, 'R'),
array('Batch No.', 30, 'C'),
array('Qty Request', 30, 'R'),
);
// Header kolom
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->SetDrawColor(0, 0, 0);
foreach ($cols as $c) {
$pdf->Cell($c[1], 7, $c[0], 1, 0, 'C', true);
}
$pdf->Ln();
// Baris data
$pdf->SetLineWidth(0.2);
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
$total_qty = 0;
$total_req = 0;
foreach ($details as $d) {
$qty = floatval($d['T_ItemOutDetailQty'] ?? 0);
$qtyReq = floatval($d['T_ItemOutDetailRequestItemOutDetailID'] > 0 ? $qty : 0);
$batchNo = $d['T_ItemOutDetailItemBatchNo'] ?? '';
// Jika batch no berupa JSON array, decode untuk ditampilkan
$batchDisplay = $batchNo;
if (strpos($batchNo, '[') === 0) {
$decoded = json_decode($batchNo, true);
if (is_array($decoded) && count($decoded) > 0) {
$parts = array();
foreach ($decoded as $b) {
$parts[] = isset($b['batchNo']) ? $b['batchNo'] : (isset($b['BatchNo']) ? $b['BatchNo'] : (is_string($b) ? $b : '-'));
}
$batchDisplay = implode(', ', $parts);
}
}
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($qty), 1, 0, 'R');
$pdf->Cell($cols[4][1], 6, $batchDisplay, 1, 0, 'C');
$reqQty = isset($d['RequestQty']) ? floatval($d['RequestQty']) : 0;
$pdf->Cell($cols[5][1], 6, $reqQty > 0 ? $this->_fmt_qty($reqQty) : '-', 1, 0, 'R');
$pdf->Ln();
$total_qty += $qty;
if ($reqQty > 0) {
$total_req += $reqQty;
}
$no++;
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 4), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[4][1], 7, '', 1, 0, 'C', true);
$pdf->Cell($cols[5][1], 7, $this->_fmt_qty($total_qty), 1, 0, 'R', true);
$pdf->Ln();
$pdf->Ln(4);
}
// =========================================================================
// PDF SECTION: Syarat & Ketentuan
// =========================================================================
private function _pdf_terms()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$terms = array(
'Barang yang dikeluarkan harus sesuai dengan jumlah dan spesifikasi yang tercantum dalam dokumen ini.',
'Penerima barang wajib memeriksa dan memverifikasi kesesuaian barang sebelum menerima.',
'Dokumen ini merupakan bukti sah pengeluaran barang dari gudang.',
'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan administrasi perusahaan.',
);
$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, ',', '.');
}
}

View File

@@ -182,17 +182,47 @@ class Rpt_pr_direct extends MY_Controller
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(2);
// ── Info dokumen: 2 kolom (kiri & kanan) ─────────────────────────────
$half = $pageW / 2;
$lbl = 30;
$val = $half - $lbl - 4;
$startY = $pdf->GetY();
$half = $pageW / 2;
$lbl = 28;
$val = $half - $lbl - 4;
// ── Kolom Kiri ────────────────────────────────────────────────────────
$pdf->SetY($startY);
$left = array(
array('Nomor', $header['PurchaseRequestDirectNumber']),
array('Tanggal', $this->_fmt_date($header['PurchaseRequestDirectDate'])),
array('Tanggal Pelaksanaan', $this->_fmt_date($header['PurchaseRequestDirectDateUse'])),
array('Status', $header['PurchaseRequestDirectStatus']),
array('Nomor', $header['PurchaseRequestDirectNumber'], true),
array('Tanggal', $this->_fmt_date($header['PurchaseRequestDirectDate']), false),
array('Tanggal Pelaksanaan', $this->_fmt_date($header['PurchaseRequestDirectDateUse']), false),
);
foreach ($left as $item) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lbl, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
if (isset($item[2]) && $item[2]) {
$pdf->SetFont('Arial_Narrow', 'B', 9);
} else {
$pdf->SetFont('Arial_Narrow', '', 9);
}
$pdf->Cell($val, 5, $item[1], 0, 1, 'L');
}
// Keterangan langsung di bawah info kiri (tanpa spasi vertikal)
if (!empty($header['PurchaseRequestDirectDescription'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lbl, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($val, 5, $header['PurchaseRequestDirectDescription'], 0, 'L');
}
$leftY = $pdf->GetY();
// ── Kolom Kanan ───────────────────────────────────────────────────────
$pdf->SetY($startY);
$rightX = 15 + $half;
$right = array(
array('Cabang', $header['M_BranchName']),
array('Regional', $header['S_RegionalName']),
@@ -200,43 +230,22 @@ class Rpt_pr_direct extends MY_Controller
array('Dibuat Oleh', $header['CreatedByName']),
);
$maxRow = max(count($left), count($right));
for ($i = 0; $i < $maxRow; $i++) {
// kolom kiri
if (isset($left[$i])) {
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lbl, 5, $left[$i][0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
if ($left[$i][0] === 'Nomor') {
$pdf->SetFont('Arial_Narrow', 'B', 9);
} else {
$pdf->SetFont('Arial_Narrow', '', 9);
}
$pdf->Cell($val, 5, $left[$i][1], 0, 0, 'L');
} else {
$pdf->Cell($half, 5, '', 0, 0);
}
// kolom kanan
if (isset($right[$i])) {
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lbl, 5, $right[$i][0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($val, 5, $right[$i][1], 0, 1, 'L');
} else {
$pdf->Cell($half, 5, '', 0, 1);
}
}
// Keterangan (catatan dihapus)
if (!empty($header['PurchaseRequestDirectDescription'])) {
foreach ($right as $item) {
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lbl, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell($lbl, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->Cell($pageW - $lbl - 4, 5, $header['PurchaseRequestDirectDescription'], 0, 1, 'L');
$pdf->SetFont('Arial_Narrow', '', 9);
if ($item[0] === 'Alamat') {
$pdf->MultiCell($val, 5, $item[1], 0, 'L');
} else {
$pdf->Cell($val, 5, $item[1], 0, 1, 'L');
}
}
$rightY = $pdf->GetY();
$pdf->Ln(4);
$pdf->SetY(max($leftY, $rightY) + 4);
}
// =========================================================================

View File

@@ -202,7 +202,7 @@ class Rpt_pr_direct_approval extends MY_Controller
array('Nomor', $header['PurchaseRequestDirectNumber'], true),
array('Tanggal', $this->_fmt_date($header['PurchaseRequestDirectDate']), false),
array('Tanggal Pelaksanaan', $this->_fmt_date($header['PurchaseRequestDirectDateUse']), false),
array('Status', $header['PurchaseRequestDirectStatus'], false),
array('Tanggal Approved', $this->_fmt_datetime($header['PurchaseRequestDirectApprovedDate'])),
);
foreach ($leftItems as $item) {
@@ -210,7 +210,7 @@ class Rpt_pr_direct_approval extends MY_Controller
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
if ($item[2]) {
if (isset($item[2]) && $item[2]) {
$pdf->SetFont('Arial_Narrow', 'B', 9);
} else {
$pdf->SetFont('Arial_Narrow', '', 9);
@@ -245,7 +245,6 @@ class Rpt_pr_direct_approval extends MY_Controller
array('Alamat', $header['M_BranchAddress']),
array('Dibuat Oleh', $header['CreatedByName']),
array('Disetujui Oleh', $header['ApprovedByName']),
array('Tanggal Approved', $this->_fmt_datetime($header['PurchaseRequestDirectApprovedDate'])),
);
foreach ($rightItems as $item) {

View File

@@ -225,9 +225,8 @@ class Rpt_purchase_order_asset extends MY_Controller
}
$leftItems = array(
array('Nomor PO', $header['PurchaseOrderNumber'], true),
array('Tanggal PO', $this->_fmt_date($header['PurchaseOrderDate']), false),
array('No. Referensi', $header['PurchaseOrderRefNumber'] ?: '-', false),
array('Nomor', $header['PurchaseOrderNumber'], true),
array('Tanggal', $this->_fmt_date($header['PurchaseOrderDate']), false),
array('Nama Kontrak', $header['ContractName'] ?: '-', false),
array('Tanggal Kontrak', $this->_fmt_date($header['ContractDate']), false),
array('Periode Kontrak', $periodeKontrak, false),

View File

@@ -6,7 +6,7 @@ require_once(APPPATH . 'libraries/fpdf/fpdf.php');
// =============================================================================
// Custom FPDF: override Footer() agar tampil otomatis di SETIAP halaman
// =============================================================================
class MutasiHandoverFpdf extends FPDF
class PurchaseOrderJasaFpdf extends FPDF
{
public $printUsername = '-';
public $printDate = '';
@@ -14,14 +14,14 @@ class MutasiHandoverFpdf extends FPDF
public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
{
parent::__construct($orientation, $unit, $size);
// Daftarkan Arial Narrow
// Daftarkan Arial Narrow — file font ada di fpdf/font/Arial_Narrow.php
$this->AddFont('Arial_Narrow', '', 'Arial_Narrow.php'); // regular
$this->AddFont('Arial_Narrow', 'B', 'Arial_Narrow_B.php'); // bold
$this->AddFont('Arial_Narrow', 'B', 'Arial_Narrow_B.php'); // bold (Liberation Sans Narrow Bold)
}
public function Footer()
{
$pageW = $this->GetPageWidth() - 30; // margin 15+15
$pageW = $this->GetPageWidth() - 30; // sama dengan margin 15+15
// ── Posisi: 20mm dari bawah halaman ──────────────────────────────────
$this->SetY(-20);
@@ -45,10 +45,10 @@ class MutasiHandoverFpdf extends FPDF
// =============================================================================
// Controller
// =============================================================================
class Rpt_penyerahan_mutasi extends MY_Controller
class Rpt_purchase_order_jasa extends MY_Controller
{
// ── Properti bersama antar fungsi PDF ─────────────────────────────────────
/** @var MutasiHandoverFpdf */
/** @var PurchaseOrderJasaFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
@@ -61,12 +61,12 @@ class Rpt_penyerahan_mutasi extends MY_Controller
public function index()
{
echo "Mutasi Handover (Penyerahan Mutasi) Report API";
echo "Purchase Order Jasa Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (MutasiHandoverID), username (opsional)
// GET/POST: id (PurchaseOrderID), username (opsional)
// =========================================================================
public function pdf()
{
@@ -84,7 +84,7 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$details = $this->_get_detail($id);
// ── Inisialisasi FPDF custom ──────────────────────────────────────
$this->_pdf = new MutasiHandoverFpdf('P', 'mm', 'A4');
$this->_pdf = new PurchaseOrderJasaFpdf('P', 'mm', 'A4');
$this->_pdf->printUsername = $username !== '' ? $username : '-';
$this->_pdf->printDate = date('d-m-Y H:i:s');
$this->_pageW = $this->_pdf->GetPageWidth() - 30;
@@ -102,7 +102,7 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$this->_pdf_terms();
// ── Output ────────────────────────────────────────────────────────
$filename = 'PH_' . str_replace('/', '-', $header['MutasiHandoverNumber']) . '.pdf';
$filename = 'PO_JASA_' . str_replace('/', '-', $header['PurchaseOrderNumber']) . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Cache-Control: private, max-age=0, must-revalidate');
@@ -120,44 +120,31 @@ class Rpt_penyerahan_mutasi extends MY_Controller
{
$sql = "
SELECT
mh.*,
mr.MutasiRequestNumber,
ic.itemCategoryID,
ic.itemCategoryName,
a.M_BranchID AS branch_asal_id,
a.M_BranchCode AS branch_asal_code,
a.M_BranchName AS branch_asal_name,
b.M_BranchID AS branch_tujuan_id,
b.M_BranchCode AS branch_tujuan_code,
b.M_BranchName AS branch_tujuan_name,
IFNULL(u.M_UserUsername, '') AS CreatedByName,
IFNULL(ur.M_UserUsername, '') AS ReceiveByName,
CASE
WHEN wh.WarehouseType = 'B' THEN CONCAT(wh.WarehouseCode, ' ', wh.WarehouseName, ' - ', b.M_BranchName)
ELSE wh.WarehouseName
END AS WarehouseName,
r.M_RuanganName
FROM mutasi_handover mh
LEFT JOIN mutasi_request mr ON mr.MutasiRequestID = mh.MutasiHandoverMutasiRequestID
JOIN item_category ic ON ic.itemCategoryID = mh.MutasiHandoverItemCategoryID
AND ic.itemCategoryIsActive = 'Y'
JOIN m_branch a ON a.M_BranchID = mh.MutasiHandoverFromBranchID
AND a.M_BranchIsActive = 'Y'
JOIN m_branch b ON b.M_BranchID = mh.MutasiHandoverToBranchID
AND b.M_BranchIsActive = 'Y'
LEFT JOIN m_user u ON u.M_UserID = mh.MutasiHandoverUserID
LEFT JOIN m_user ur ON ur.M_UserID = mh.MutasiHandoverReceiveUserID
LEFT JOIN warehouse wh ON wh.WarehouseID = mh.MutasiHandoverReceiveWarehouseID
AND wh.WarehouseIsActive = 'Y'
LEFT JOIN m_ruangan r ON r.M_RuanganID = mh.MutasiHandoverReceiveM_RuanganID
AND r.M_RuanganIsActive = 'Y'
WHERE mh.MutasiHandoverID = ?
AND mh.MutasiHandoverIsActive = 'Y'
po.*,
sup.SupplierName,
sup.SupplierAddress,
sup.SupplierPhone,
b.M_BranchName,
b.M_BranchAddress,
r.S_RegionalName,
kj.T_KontrakJasaJenisKontrak AS JenisKontrak,
kj.T_KontrakJasaStartDate AS StartDate,
kj.T_KontrakJasaEndDate AS EndDate,
kj.T_KontrakJasaJumlahPI AS JumlahPI,
kj.T_KontrakJasaStatus AS StatusKontrak
FROM purchase_order po
LEFT JOIN supplier sup ON sup.SupplierID = po.PurchaseOrderSupplierID
LEFT JOIN s_regional r ON r.S_RegionalID = po.PurchaseOrderS_RegionalID
LEFT JOIN t_kontrak_jasa kj ON kj.T_KontrakJasaPurchaseOrderID = po.PurchaseOrderID
AND kj.T_KontrakJasaIsActive = 'Y'
LEFT JOIN m_branch b ON b.M_BranchCode = kj.T_KontrakJasaM_BranchCode
WHERE po.PurchaseOrderIsActive = 'Y'
AND po.PurchaseOrderID = ?
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Penyerahan Mutasi tidak ditemukan");
$this->sys_error("Data Purchase Order Jasa tidak ditemukan");
exit;
}
return $qry->row_array();
@@ -170,17 +157,22 @@ class Rpt_penyerahan_mutasi extends MY_Controller
{
$sql = "
SELECT
mhd.*,
i.M_ItemCode,
i.M_ItemDesc,
pos.PurchaseOrderSummaryQty AS RequestQty,
pos.PurchaseOrderSummaryPrice AS SupplierPrice,
pos.PurchaseOrderSummaryDiscountAmount AS DiskonAmount,
pos.PurchaseOrderSummaryTotal AS TempTotal,
item.M_ItemCode,
item.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode
FROM mutasi_handover_detail mhd
LEFT JOIN m_item i ON i.M_ItemID = mhd.MutasiHandoverDetailM_ItemID
LEFT JOIN itemunit iu ON iu.ItemUnitID = mhd.MutasiHandoverDetailItemUnitID
WHERE mhd.MutasiHandoverDetailMutasiHandoverID = ?
AND mhd.MutasiHandoverDetailIsActive = 'Y'
ORDER BY mhd.MutasiHandoverDetailID ASC
FROM purchase_order_summary pos
JOIN m_item item ON item.M_ItemID = pos.PurchaseOrderSummaryItemID
AND item.M_ItemIsActive = 'Y'
LEFT JOIN itemunit iu ON iu.ItemUnitID = pos.PurchaseOrderSummaryItemUnitID
AND iu.ItemUnitIsActive = 'Y'
WHERE pos.PurchaseOrderSummaryIsActive = 'Y'
AND pos.PurchaseOrderSummaryPurchaseOrderID = ?
ORDER BY pos.PurchaseOrderSummaryID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
@@ -197,7 +189,7 @@ class Rpt_penyerahan_mutasi extends MY_Controller
// ── Judul utama ───────────────────────────────────────────────────────
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'PENYERAHAN MUTASI (HANDOVER)', 0, 1, 'L');
$pdf->Cell($pageW, 8, 'PURCHASE ORDER JASA (SERVICE PO)', 0, 1, 'L');
$pdf->SetDrawColor(0, 0, 0);
$pdf->SetLineWidth(0.5);
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
@@ -205,18 +197,34 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$startY = $pdf->GetY();
$halfW = $pageW / 2;
$lblW = 30;
$lblW = 28;
$valW = $halfW - $lblW - 4;
// ── Kolom Kiri ────────────────────────────────────────────────────────
$pdf->SetY($startY);
// Format Jenis Kontrak
$jenisKontrak = '-';
if (!empty($header['JenisKontrak'])) {
$jenisKontrak = ($header['JenisKontrak'] === 'once') ? 'Sekali Bayar (Once)' : 'Berkala (Recurring)';
}
// Periode kontrak
$periodeJasa = '-';
if (!empty($header['StartDate']) && $header['StartDate'] !== '0000-00-00') {
$periodeJasa = $this->_fmt_date($header['StartDate']) . ' s/d ' . $this->_fmt_date($header['EndDate']);
}
// Jumlah Termin/PI
$jumlahPI = '-';
if (intval($header['JumlahPI'] ?? 0) > 0) {
$jumlahPI = $header['JumlahPI'] . ' Kali Pembayaran';
}
$leftItems = array(
array('Nomor Handover', $header['MutasiHandoverNumber'], true),
array('No. Request', $header['MutasiRequestNumber'] ?: '-', false),
array('Tanggal', $this->_fmt_date($header['MutasiHandoverDate']), false),
array('Status', $header['MutasiHandoverStatus'], false),
array('Kategori', $header['itemCategoryName'], false),
array('Nomor', $header['PurchaseOrderNumber'], true),
array('Tanggal', $this->_fmt_date($header['PurchaseOrderDate']), false),
array('Cabang', $header['M_BranchName'] ?: '-', false),
);
foreach ($leftItems as $item) {
@@ -232,13 +240,13 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$pdf->Cell($valW, 5, $item[1], 0, 1, 'L');
}
// Keterangan (Note) langsung di bawah Status
if (!empty($header['MutasiHandoverNote'])) {
// Keterangan / Note langsung di bawah (tanpa spasi vertikal)
if (!empty($header['PurchaseOrderNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $header['MutasiHandoverNote'], 0, 'L');
$pdf->MultiCell($valW, 5, $header['PurchaseOrderNote'], 0, 'L');
}
$leftY = $pdf->GetY();
@@ -247,11 +255,12 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$rightX = 15 + $halfW;
$rightItems = array(
array('Cabang Asal', $header['branch_asal_name']),
array('Cabang Tujuan', $header['branch_tujuan_name']),
array('Warehouse Tujuan', $header['WarehouseName'] ?: '-'),
array('Ruangan Tujuan', $header['M_RuanganName'] ?: '-'),
array('Dibuat Oleh', $header['CreatedByName']),
array('Supplier', $header['SupplierName'] ?: '-'),
array('Alamat Supplier', $header['SupplierAddress'] ?: '-'),
array('Telp Supplier', $header['SupplierPhone'] ?: '-'),
array('Jenis Kontrak', $jenisKontrak),
array('Periode Jasa', $periodeJasa),
array('Termin Bayar', $jumlahPI),
);
foreach ($rightItems as $item) {
@@ -260,27 +269,13 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$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');
if ($item[0] === 'Alamat Supplier') {
$pdf->MultiCell($valW, 5, $item[1], 0, 'L');
} else {
$pdf->Cell($valW, 5, $item[1], 0, 1, 'L');
}
}
// Tanggal terima di kolom kanan
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Tgl Terima', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $this->_fmt_date($header['MutasiHandoverReceiveDate']), 0, 1, 'L');
// Penerima di kolom kanan
if ($header['MutasiHandoverStatus'] === 'Received') {
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Diterima Oleh', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $header['ReceiveByName'] ?: '-', 0, 1, 'L');
}
$rightY = $pdf->GetY();
// Posisikan Y ke yang paling bawah + margin
@@ -288,7 +283,7 @@ class Rpt_penyerahan_mutasi extends MY_Controller
}
// =========================================================================
// PDF SECTION: Data — Tabel detail item + ringkasan nilai
// PDF SECTION: Data — Tabel detail item
// =========================================================================
private function _pdf_data($details)
{
@@ -298,18 +293,19 @@ class Rpt_penyerahan_mutasi extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 80 + 20 + 20 + 25 + 27 = 180
// 8 + 65 + 20 + 15 + 25 + 22 + 25 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama Item', 80, 'L'),
array('Unit', 20, 'C'),
array('Qty', 20, 'R'),
array('Nilai Buku', 25, 'R'),
array('Total', 27, 'R'),
array('No', 8, 'C'),
array('Nama', 65, 'L'),
array('Unit', 20, 'C'),
array('Qty', 15, 'R'),
array('Harga Satuan', 25, 'R'),
array('Diskon', 22, 'R'),
array('Total', 25, 'R'),
);
// Header kolom
$pdf->SetLineWidth(0.3);
$pdf->SetLineWidth(0.3); // border medium
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->SetDrawColor(0, 0, 0);
@@ -319,57 +315,71 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$pdf->Ln();
// Baris data
$pdf->SetLineWidth(0.2);
$pdf->SetLineWidth(0.2); // border lebih tipis di data
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
$total_val = 0;
$subTotal = 0;
foreach ($details as $d) {
$bookValue = floatval($d['MutasiHandoverDetailBookValue'] ?? 0);
$qty = floatval($d['MutasiHandoverDetailQty'] ?? 0);
$lineTotal = $qty * $bookValue;
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($qty), 1, 0, 'R');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($bookValue), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($lineTotal), 1, 0, 'R');
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($d['RequestQty']), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($d['SupplierPrice']), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['DiskonAmount']), 1, 0, 'R');
$pdf->Cell($cols[6][1], 6, $this->_fmt_rp($d['TempTotal']), 1, 0, 'R');
$pdf->Ln();
$total_val += $lineTotal;
$subTotal += floatval($d['TempTotal']);
$no++;
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 5), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 9);
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[5][1], 7, $this->_fmt_rp($total_val), 1, 0, 'R', true);
$pdf->Ln();
$pdf->Ln(2);
// ── Ringkasan Biaya di Bagian Kanan Bawah ──────────────────────────────
$discountPO = floatval($header['PurchaseOrderDiscountAmount'] ?? 0);
$ppnAmount = floatval($header['PurchaseOrderTaxAmountPpn'] ?? 0);
$pphAmount = floatval($header['PurchaseOrderTaxAmountPph'] ?? 0);
$shipCost = floatval($header['PurchaseOrderShippingCost'] ?? 0);
$grandTotal = floatval($header['PurchaseOrderGrandTotal'] ?? ($subTotal - $discountPO + $ppnAmount - $pphAmount + $shipCost));
// ── Ringkasan nilai (rata kanan) ──────────────────────────────────────
$pdf->Ln(3);
$summary = array(
array('Total Nilai Penyerahan', $this->_fmt_rp($header['MutasiHandoverTotalValue'])),
array('Subtotal', $this->_fmt_rp($subTotal)),
);
$cW1 = 50;
$cW2 = 40;
$offsetX = 15 + $this->_pageW - $cW1 - $cW2;
if ($discountPO > 0) {
$summary[] = array('Diskon PO', '-' . $this->_fmt_rp($discountPO));
}
if ($ppnAmount > 0) {
$summary[] = array('PPN', $this->_fmt_rp($ppnAmount));
}
if ($pphAmount > 0) {
$summary[] = array('PPH', '-' . $this->_fmt_rp($pphAmount));
}
if ($shipCost > 0) {
$summary[] = array('Ongkos Kirim', $this->_fmt_rp($shipCost));
}
$summary[] = array('Grand Total', $this->_fmt_rp($grandTotal));
$cW1 = 47;
$cW2 = 25;
$offsetX = 15 + array_sum(array_column(array_slice($cols, 0, 4), 1)); // Sejajar dengan Harga Satuan ke kanan
foreach ($summary as $s) {
$pdf->SetX($offsetX);
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->Cell($cW1, 5, $s[0], 0, 0, 'L');
$pdf->SetFont('Arial_Narrow', 'B', 8);
if ($s[0] === 'Grand Total') {
$pdf->SetFont('Arial_Narrow', 'B', 9);
} else {
$pdf->SetFont('Arial_Narrow', 'B', 8);
}
$pdf->Cell($cW2, 5, $s[1], 0, 1, 'R');
}
$pdf->Ln(4);
$pdf->Ln(8);
}
// =========================================================================
@@ -381,10 +391,10 @@ class Rpt_penyerahan_mutasi extends MY_Controller
$pageW = $this->_pageW;
$terms = array(
'Penyerahan mutasi ini dilakukan berdasarkan dokumen mutasi yang telah disetujui sebelumnya.',
'Barang yang diserahkan harus sesuai dengan jumlah dan spesifikasi yang tercantum dalam dokumen ini.',
'Penerima barang wajib memeriksa dan memverifikasi kesesuaian barang sebelum menerima.',
'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan proses mutasi barang.',
'Purchase Order (PO) Jasa ini tunduk pada syarat dan ketentuan kontrak pengerjaan yang disepakati.',
'Seluruh termin pembayaran angsuran jasa harus ditagihkan disertai dengan Berita Acara Serah Terima Jasa (BASTJ).',
'Klaim ketidaksesuaian hasil pengerjaan harus dilaporkan langsung ke vendor bersangkutan.',
'PO ini sah secara hukum setelah disetujui dan divalidasi secara digital melalui sistem ERP.'
);
$pdf->SetFont('Arial_Narrow', 'B', 9);
@@ -417,6 +427,7 @@ class Rpt_penyerahan_mutasi extends MY_Controller
return number_format(floatval($n), 2, ',', '.');
}
// format jumlah/qty: tanpa desimal
private function _fmt_qty($n)
{
return number_format(floatval($n), 0, ',', '.');

View File

@@ -221,7 +221,6 @@ class Rpt_receive_item_po extends MY_Controller
$leftItems = array(
array('Nomor', $header['ReceiveOrderPoNumber'], true),
array('Tanggal', $this->_fmt_date($header['ReceiveOrderPoIDate']), false),
array('No. Referensi', $header['ReceiveOrderPoRefNumber'] ?: '-', false),
array('No. Surat Jalan', $header['ReceiveOrderPoDONumber'] ?: '-', false),
array('Gudang Penerima', $header['ReceivedWarehouseName'] ?: '-', false),
);
@@ -290,15 +289,14 @@ class Rpt_receive_item_po extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 45 + 38 + 20 + 15 + 24 + 30 = 180
// 8 + 75 + 23 + 15 + 27 + 32 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama', 45, 'L'),
array('No. PO', 38, 'L'),
array('Unit', 20, 'C'),
array('Nama', 75, 'L'),
array('Unit', 23, 'C'),
array('Qty', 15, 'C'),
array('Harga', 24, 'R'),
array('Total', 30, 'R'),
array('Harga', 27, 'R'),
array('Total', 32, 'R'),
);
// Header kolom
@@ -321,11 +319,10 @@ class Rpt_receive_item_po extends MY_Controller
foreach ($details as $d) {
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['PurchaseOrderNumber'] ?: '-', 1, 0, 'L');
$pdf->Cell($cols[3][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_qty($d['ReceiveOrderPoDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailPrice']), 1, 0, 'R');
$pdf->Cell($cols[6][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailTotal']), 1, 0, 'R');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($d['ReceiveOrderPoDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailPrice']), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailTotal']), 1, 0, 'R');
$pdf->Ln();
$grand_tot += floatval($d['ReceiveOrderPoDetailTotal']);
@@ -333,12 +330,12 @@ class Rpt_receive_item_po extends MY_Controller
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 6), 1));
$span = array_sum(array_column(array_slice($cols, 0, 5), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 9); // bold + size 9 agar menonjol
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[6][1], 7, $this->_fmt_rp($grand_tot), 1, 0, 'R', true);
$pdf->Cell($cols[5][1], 7, $this->_fmt_rp($grand_tot), 1, 0, 'R', true);
$pdf->Ln(6);
// Ringkasan Biaya jika ada Biaya Ekspedisi (mendukung fallback dari PO)
@@ -360,9 +357,9 @@ class Rpt_receive_item_po extends MY_Controller
array('Grand Total', $this->_fmt_rp($grandTotal)),
);
$cW1 = 39;
$cW2 = 30;
$offsetX = 15 + array_sum(array_column(array_slice($cols, 0, 4), 1));
$cW1 = 27;
$cW2 = 32;
$offsetX = 15 + array_sum(array_column(array_slice($cols, 0, 4), 1));
foreach ($summary as $s) {
$pdf->SetX($offsetX);

View File

@@ -224,8 +224,6 @@ class Rpt_receive_item_po_inventaris extends MY_Controller
$leftItems = array(
array('Nomor', $header['ReceiveOrderPoNumber'], true),
array('Tanggal', $this->_fmt_date($header['ReceiveOrderPoIDate']), false),
array('No. Referensi', $header['ReceiveOrderPoRefNumber'] ?: '-', false),
array('No. Surat Jalan', $header['ReceiveOrderPoDONumber'] ?: '-', false),
array('Gudang Penerima', $header['ReceivedWarehouseName'] ?: '-', false),
array('Ruangan / Lokasi', $header['RuanganName'] ?: '-', false),
);
@@ -261,6 +259,7 @@ class Rpt_receive_item_po_inventaris extends MY_Controller
array('Supplier', $header['SupplierName'] ?: '-'),
array('Alamat Supplier', $header['SupplierAddress'] ?: '-'),
array('Telp Supplier', $header['SupplierPhone'] ?: '-'),
array('Cabang', $header['M_BranchName'] ?: '-'),
array('Alamat Penerima', $header['M_BranchAddress'] ?: '-'),
);
@@ -294,15 +293,14 @@ class Rpt_receive_item_po_inventaris extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 45 + 38 + 20 + 15 + 24 + 30 = 180
// 8 + 75 + 23 + 15 + 27 + 32 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama', 45, 'L'),
array('No. PO', 38, 'L'),
array('Unit', 20, 'C'),
array('Qty', 15, 'R'),
array('Harga', 24, 'R'),
array('Total', 30, 'R'),
array('Nama', 75, 'L'),
array('Unit', 23, 'C'),
array('Qty', 15, 'C'),
array('Harga', 27, 'R'),
array('Total', 32, 'R'),
);
// Header kolom
@@ -325,11 +323,10 @@ class Rpt_receive_item_po_inventaris extends MY_Controller
foreach ($details as $d) {
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['PurchaseOrderNumber'] ?: '-', 1, 0, 'L');
$pdf->Cell($cols[3][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_qty($d['ReceiveOrderPoDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailPrice']), 1, 0, 'R');
$pdf->Cell($cols[6][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailTotal']), 1, 0, 'R');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($d['ReceiveOrderPoDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailPrice']), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailTotal']), 1, 0, 'R');
$pdf->Ln();
$grand_tot += floatval($d['ReceiveOrderPoDetailTotal']);
@@ -337,12 +334,12 @@ class Rpt_receive_item_po_inventaris extends MY_Controller
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 6), 1));
$span = array_sum(array_column(array_slice($cols, 0, 5), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 9); // bold + size 9 agar menonjol
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[6][1], 7, $this->_fmt_rp($grand_tot), 1, 0, 'R', true);
$pdf->Cell($cols[5][1], 7, $this->_fmt_rp($grand_tot), 1, 0, 'R', true);
$pdf->Ln(6);
// Ringkasan Biaya jika ada Biaya Ekspedisi (mendukung fallback dari PO)
@@ -364,8 +361,8 @@ class Rpt_receive_item_po_inventaris extends MY_Controller
array('Grand Total', $this->_fmt_rp($grandTotal)),
);
$cW1 = 39;
$cW2 = 30;
$cW1 = 27;
$cW2 = 32;
$offsetX = 15 + array_sum(array_column(array_slice($cols, 0, 4), 1));
foreach ($summary as $s) {

View File

@@ -99,6 +99,7 @@ class Rpt_receive_order_asset extends MY_Controller
// ── Susun isi halaman ─────────────────────────────────────────────
$this->_pdf_header();
$this->_pdf_data($details);
$this->_pdf_inspeksi($details);
$this->_pdf_terms();
// ── Output ────────────────────────────────────────────────────────
@@ -177,13 +178,22 @@ class Rpt_receive_order_asset extends MY_Controller
item.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode,
po.PurchaseOrderNumber
po.PurchaseOrderNumber,
uP.M_UserUsername AS StaffPenerima,
insp.ReceiveOrderPoInspeksiPengirim AS StaffPengirim,
insp.ReceiveOrderPoInspeksiKeadaanKemasan AS KeadaanKemasan,
insp.ReceiveOrderPoInspeksiKondisiPengiriman AS KondisiPengiriman,
insp.ReceiveOrderPoInspeksiSimpulan AS Simpulan,
insp.ReceiveOrderPoInspeksiCatatan AS CatatanInspeksi
FROM receive_order_po_detail ropd
JOIN m_item item ON item.M_ItemID = ropd.ReceiveOrderPoItemID
AND item.M_ItemIsActive = 'Y'
LEFT JOIN itemunit iu ON iu.ItemUnitID = ropd.ReceiveOrderPoItemUnitID
AND iu.ItemUnitIsActive = 'Y'
LEFT JOIN purchase_order po ON po.PurchaseOrderID = ropd.ReceiveOrderPoDetailPurchaseOrderID
LEFT JOIN receive_order_po_inspeksi insp ON insp.ReceiveOrderPoInspeksiReceiveOrderPoDetailID = ropd.ReceiveOrderPoDetailID
AND insp.ReceiveOrderPoInspeksiIsActive = 'Y'
LEFT JOIN m_user uP ON uP.M_UserID = insp.ReceiveOrderPoInspeksiStaffPenerimaID
WHERE ropd.ReceiveOrderPoDetailIsActive = 'Y'
AND ropd.ReceiveOrderPoDetailReceiveOrderPoID = ?
ORDER BY ropd.ReceiveOrderPoDetailID ASC
@@ -220,7 +230,6 @@ class Rpt_receive_order_asset extends MY_Controller
$leftItems = array(
array('Nomor', $header['ReceiveOrderPoNumber'], true),
array('Tanggal', $this->_fmt_date($header['ReceiveOrderPoIDate']), false),
array('No. Referensi', $header['ReceiveOrderPoRefNumber'] ?: '-', false),
array('Gudang Penerima', $header['ReceivedWarehouseName'] ?: '-', false),
);
@@ -289,15 +298,14 @@ class Rpt_receive_order_asset extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 45 + 38 + 20 + 15 + 24 + 30 = 180
// 8 + 75 + 23 + 15 + 27 + 32 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama', 45, 'L'),
array('No. PO', 38, 'L'),
array('Unit', 20, 'C'),
array('Qty', 15, 'R'),
array('Harga', 24, 'R'),
array('Total', 30, 'R'),
array('Nama', 75, 'L'),
array('Unit', 23, 'C'),
array('Qty', 15, 'C'),
array('Harga', 27, 'R'),
array('Total', 32, 'R'),
);
// Header kolom
@@ -320,11 +328,10 @@ class Rpt_receive_order_asset extends MY_Controller
foreach ($details as $d) {
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['PurchaseOrderNumber'] ?: '-', 1, 0, 'L');
$pdf->Cell($cols[3][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_qty($d['ReceiveOrderPoDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailPrice']), 1, 0, 'R');
$pdf->Cell($cols[6][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailTotal']), 1, 0, 'R');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($d['ReceiveOrderPoDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailPrice']), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $this->_fmt_rp($d['ReceiveOrderPoDetailTotal']), 1, 0, 'R');
$pdf->Ln();
$grand_tot += floatval($d['ReceiveOrderPoDetailTotal']);
@@ -332,13 +339,12 @@ class Rpt_receive_order_asset extends MY_Controller
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 6), 1));
$span = array_sum(array_column(array_slice($cols, 0, 5), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 9); // bold + size 9 agar menonjol
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[6][1], 7, $this->_fmt_rp($grand_tot), 1, 0, 'R', true);
$pdf->Ln(6);
$pdf->Cell($cols[5][1], 7, $this->_fmt_rp($grand_tot), 1, 0, 'R', true);
// Ringkasan Biaya jika ada Biaya Ekspedisi (mendukung fallback dari PO)
$shippingCost = floatval($header['ReceiveOrderShippingCostAmount'] ?? 0);
@@ -350,7 +356,7 @@ class Rpt_receive_order_asset extends MY_Controller
}
if ($shippingCost > 0) {
$pdf->Ln(2);
$pdf->Ln(7);
$grandTotal = $grand_tot + $shippingCost;
$statusPaid = ($isPaid === 'Y') ? ' (Lunas)' : ' (Belum Lunas)';
$summary = array(
@@ -359,8 +365,8 @@ class Rpt_receive_order_asset extends MY_Controller
array('Grand Total', $this->_fmt_rp($grandTotal)),
);
$cW1 = 39;
$cW2 = 30;
$cW1 = 27;
$cW2 = 32;
$offsetX = 15 + array_sum(array_column(array_slice($cols, 0, 4), 1));
foreach ($summary as $s) {
@@ -370,9 +376,79 @@ class Rpt_receive_order_asset extends MY_Controller
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->Cell($cW2, 5, $s[1], 0, 1, 'R');
}
$pdf->Ln(8);
} else {
$pdf->Ln(8);
}
}
// PDF SECTION: Inspeksi & Verifikasi Barang / Aset (Blok Terpisah)
// =========================================================================
private function _pdf_inspeksi($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
// Cek apakah ada data inspeksi yang terisi
$hasInspeksi = false;
foreach ($details as $d) {
if (!empty($d['Simpulan']) || !empty($d['StaffPenerima']) || !empty($d['CatatanInspeksi'])) {
$hasInspeksi = true;
break;
}
}
$pdf->Ln(8);
if (!$hasInspeksi) {
return;
}
$pdf->Ln(2);
$pdf->SetFont('Arial_Narrow', 'B', 10);
$pdf->Cell($pageW, 6, 'DETAIL HASIL INSPEKSI BARANG / ASET', 0, 1, 'L');
$pdf->SetLineWidth(0.3);
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(2);
$no = 1;
foreach ($details as $d) {
// Lewati jika item ini tidak memiliki data inspeksi sama sekali
if (empty($d['Simpulan']) && empty($d['StaffPenerima']) && empty($d['CatatanInspeksi'])) {
continue;
}
$pdf->SetFont('Arial_Narrow', 'B', 9);
$pdf->Cell($pageW, 5, $no . '. ' . ($d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-')), 0, 1, 'L');
// Indentasi isi detail inspeksi
$lblW = 32;
$valW = $pageW - $lblW - 4;
$items = array(
array('Staff Penerima', $d['StaffPenerima'] ?: '-'),
array('Kurir Pengirim', $d['StaffPengirim'] ?: '-'),
array('Keadaan Kemasan', $d['KeadaanKemasan'] ?: '-'),
array('Kondisi Pengiriman', $d['KondisiPengiriman'] ?: '-'),
array('Simpulan Hasil', $d['Simpulan'] ?: '-'),
array('Catatan Inspeksi', $d['CatatanInspeksi'] ?: '-'),
);
foreach ($items as $item) {
$pdf->SetX(18); // Indent slightly to the right
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $item[1], 0, 'L');
}
$pdf->Ln(2);
$pdf->SetDrawColor(200, 200, 200);
$pdf->SetLineWidth(0.1);
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(2);
$pdf->SetDrawColor(0, 0, 0); // restore black
$no++;
}
}
// =========================================================================

View File

@@ -0,0 +1,600 @@
<?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 ReceiveOrderJasaFpdf extends FPDF
{
public $printUsername = '-';
public $printDate = '';
// Properties untuk tabel multiline
public $widths;
public $aligns;
public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
{
parent::__construct($orientation, $unit, $size);
// Daftarkan Arial Narrow — file font ada di fpdf/font/Arial_Narrow.php
$this->AddFont('Arial_Narrow', '', 'Arial_Narrow.php'); // regular
$this->AddFont('Arial_Narrow', 'B', 'Arial_Narrow_B.php'); // bold (Liberation Sans Narrow Bold)
}
public function SetWidths($w)
{
$this->widths = $w;
}
public function SetAligns($a)
{
$this->aligns = $a;
}
public function Row($data, $fill = false, $h = 6)
{
// Hitung tinggi maksimum baris berdasarkan multiline
$nb = 0;
for ($i = 0; $i < count($data); $i++) {
$nb = max($nb, $this->NbLines($this->widths[$i], $data[$i]));
}
$rowH = $h * $nb;
// Cek apakah perlu ganti halaman secara otomatis
$this->CheckPageBreak($rowH);
// Gambar cell pada baris
for ($i = 0; $i < count($data); $i++) {
$w = $this->widths[$i];
$a = isset($this->aligns[$i]) ? $this->aligns[$i] : 'L';
$x = $this->GetX();
$y = $this->GetY();
// Gambar border dan background
$this->Rect($x, $y, $w, $rowH, $fill ? 'DF' : 'D');
// Tulis teks menggunakan MultiCell
$this->MultiCell($w, $h, $data[$i], 0, $a);
// Geser posisi X ke kanan untuk cell berikutnya
$this->SetXY($x + $w, $y);
}
// Pindah baris
$this->Ln($rowH);
}
public function CheckPageBreak($h)
{
// Jika tinggi baris melewati batas, buat halaman baru
if ($this->GetY() + $h > $this->PageBreakTrigger) {
$this->AddPage($this->CurOrientation);
}
}
public function NbLines($w, $txt)
{
// Menghitung jumlah baris yang akan dihasilkan oleh MultiCell
$cw = &$this->CurrentFont['cw'];
if ($w == 0) {
$w = $this->w - $this->rMargin - $this->x;
}
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
$s = str_replace("\r", '', $txt);
$nb = strlen($s);
if ($nb > 0 && $s[$nb - 1] == "\n") {
$nb--;
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while ($i < $nb) {
$c = $s[$i];
if ($c == "\n") {
$i++;
$sep = -1;
$j = $i;
$l = 0;
$nl++;
continue;
}
if ($c == ' ') {
$sep = $i;
}
$l += $cw[$c];
if ($l > $wmax) {
if ($sep == -1) {
if ($i == $j) {
$i++;
}
} else {
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
$nl++;
} else {
$i++;
}
}
return $nl;
}
public function Footer()
{
$pageW = $this->GetPageWidth() - 30; // sama dengan 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_receive_order_jasa extends MY_Controller
{
// ── Properti bersama antar fungsi PDF ─────────────────────────────────────
/** @var ReceiveOrderJasaFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
private $_username;
public function __construct()
{
parent::__construct();
}
public function index()
{
echo "Receive Order Jasa Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (ReceiveOrderPoID), username (opsional)
// =========================================================================
public function pdf()
{
try {
$id = intval($this->input->get_post('id'));
$username = trim($this->input->get_post('username') ?? '');
if ($id <= 0) {
$this->sys_error("ID tidak valid");
exit;
}
// ── Ambil data header & detail dari DB ────────────────────────────
$header = $this->_get_header($id);
$details = $this->_get_detail($id);
// ── Inisialisasi FPDF custom ──────────────────────────────────────
$this->_pdf = new ReceiveOrderJasaFpdf('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->_header_data = $header;
$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($details);
$this->_pdf_inspeksi($details);
$this->_pdf_terms();
// ── Output ────────────────────────────────────────────────────────
$filename = 'RO_JASA_' . str_replace('/', '-', $header['ReceiveOrderPoNumber']) . '.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 header
// =========================================================================
private function _get_header($id)
{
$sql = "
SELECT
ro.ReceiveOrderPoID,
ro.ReceiveOrderPoNumber,
ro.ReceiveOrderPoIDate,
ro.ReceiveOrderPoRefNumber,
ro.ReceiveOrderPoNote,
ro.ReceiveOrderPoConfirmed,
sup.SupplierName,
sup.SupplierAddress,
sup.SupplierPhone,
b.M_BranchName,
b.M_BranchAddress,
po.PurchaseOrderNumber,
kj.T_KontrakJasaJenisKontrak AS JenisKontrak,
kj.T_KontrakJasaStartDate AS StartDate,
kj.T_KontrakJasaEndDate AS EndDate,
kj.T_KontrakJasaJumlahPI AS JumlahPI,
kj.T_KontrakJasaUsedCount AS UsedCount
FROM receive_order_po ro
JOIN receive_order_po_detail rod ON rod.ReceiveOrderPoDetailReceiveOrderPoID = ro.ReceiveOrderPoID
AND rod.ReceiveOrderPoDetailIsActive = 'Y'
LEFT JOIN purchase_order po ON po.PurchaseOrderID = rod.ReceiveOrderPoDetailPurchaseOrderID
LEFT JOIN supplier sup ON sup.SupplierID = ro.ReceiveOrderPoSupplierID
LEFT JOIN t_kontrak_jasa kj ON kj.T_KontrakJasaPurchaseOrderID = rod.ReceiveOrderPoDetailPurchaseOrderID
AND kj.T_KontrakJasaM_BranchCode = ro.ReceiveOrderPoM_BranchCode
AND kj.T_KontrakJasaIsActive = 'Y'
LEFT JOIN m_branch b ON b.M_BranchCode = ro.ReceiveOrderPoM_BranchCode
WHERE ro.ReceiveOrderPoIsActive = 'Y'
AND ro.ReceiveOrderPoID = ?
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Receive Order Jasa tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
rod.ReceiveOrderPoDetailID,
rod.ReceiveOrderPoDetailQty AS Qty,
rod.ReceiveOrderPoDetailPrice AS Price,
(rod.ReceiveOrderPoDetailQty * rod.ReceiveOrderPoDetailPrice) AS TotalPrice,
item.M_ItemCode,
item.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode,
insp.OrderJasaInspeksiHasilPengerjaan AS HasilPengerjaan,
insp.OrderJasaInspeksiCatatanPengerjaan AS CatatanPengerjaan,
insp.OrderJasaInspeksiKesimpulan AS Kesimpulan,
insp.OrderJasaInspeksiCatatan AS CatatanKesimpulan,
insp.OrderJasaInspeksiPetugasPengerjaan AS Pekerja,
uP.M_UserUsername AS PemeriksaName
FROM receive_order_po_detail rod
JOIN m_item item ON item.M_ItemID = rod.ReceiveOrderPoItemID
AND item.M_ItemIsActive = 'Y'
LEFT JOIN itemunit iu ON iu.ItemUnitID = rod.ReceiveOrderPoItemUnitID
AND iu.ItemUnitIsActive = 'Y'
LEFT JOIN order_jasa_inspeksi insp ON insp.OrderJasaInspeksiReceiveOrderPoDetailID = rod.ReceiveOrderPoDetailID
AND insp.OrderJasaInspeksiIsActive = 'Y'
LEFT JOIN m_user uP ON uP.M_UserID = insp.OrderJasaInspeksiStaffPemeriksaID
WHERE rod.ReceiveOrderPoDetailIsActive = 'Y'
AND rod.ReceiveOrderPoDetailReceiveOrderPoID = ?
ORDER BY rod.ReceiveOrderPoDetailID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
}
// =========================================================================
// PDF SECTION: Header — Judul + Informasi Dokumen (2 kolom)
// =========================================================================
private function _pdf_header()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// ── Judul utama ───────────────────────────────────────────────────────
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'TANDA TERIMA JASA (RECEIVE ORDER SERVICE)', 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 = 28;
$valW = $halfW - $lblW - 4;
// ── Kolom Kiri ────────────────────────────────────────────────────────
$pdf->SetY($startY);
// Format Jenis Kontrak
$jenisKontrak = '-';
if (!empty($header['JenisKontrak'])) {
$jenisKontrak = ($header['JenisKontrak'] === 'once') ? 'Sekali Bayar (Once)' : 'Berkala (Recurring)';
}
// Periode kontrak
$periodeJasa = '-';
if (!empty($header['StartDate']) && $header['StartDate'] !== '0000-00-00') {
$periodeJasa = $this->_fmt_date($header['StartDate']) . ' s/d ' . $this->_fmt_date($header['EndDate']);
}
// Termin / usedcount
$terminInfo = '-';
if (intval($header['JumlahPI'] ?? 0) > 0) {
$terminInfo = "Termin ke-" . ($header['UsedCount'] ?? 0) . " dari " . $header['JumlahPI'];
}
$leftItems = array(
array('Nomor', $header['ReceiveOrderPoNumber'], true),
array('Tanggal', $this->_fmt_date($header['ReceiveOrderPoIDate']), false),
array('Cabang', $header['M_BranchName'] ?: '-', false),
);
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');
}
// Keterangan / Note langsung di bawah (tanpa spasi vertikal)
if (!empty($header['ReceiveOrderPoNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $header['ReceiveOrderPoNote'], 0, 'L');
}
$leftY = $pdf->GetY();
// ── Kolom Kanan ───────────────────────────────────────────────────────
$pdf->SetY($startY);
$rightX = 15 + $halfW;
$rightItems = array(
array('Supplier', $header['SupplierName'] ?: '-'),
array('Alamat Supplier', $header['SupplierAddress'] ?: '-'),
array('Telp Supplier', $header['SupplierPhone'] ?: '-'),
array('Jenis Kontrak', $jenisKontrak),
array('Periode Jasa', $periodeJasa),
array('Termin Realisasi', $terminInfo),
);
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);
if ($item[0] === 'Alamat Supplier') {
$pdf->MultiCell($valW, 5, $item[1], 0, 'L');
} else {
$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 detail item
// =========================================================================
private function _pdf_data($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 75 + 20 + 15 + 30 + 32 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama', 75, 'L'),
array('Unit', 20, 'C'),
array('Qty', 15, 'C'),
array('Harga', 30, 'R'),
array('Total', 32, 'R'),
);
// Header kolom
$pdf->SetLineWidth(0.3); // border medium
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->SetDrawColor(0, 0, 0);
foreach ($cols as $c) {
$pdf->Cell($c[1], 7, $c[0], 1, 0, 'C', true);
}
$pdf->Ln();
// Set lebar dan alignment kolom untuk metode Row()
$pdf->SetWidths(array_column($cols, 1));
$pdf->SetAligns(array_column($cols, 2));
// Baris data
$pdf->SetLineWidth(0.2); // border lebih tipis di data
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
$totalPay = 0;
foreach ($details as $d) {
$row_data = array(
$no,
$d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'),
$d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'),
$this->_fmt_qty($d['Qty']),
$this->_fmt_rp($d['Price']),
$this->_fmt_rp($d['TotalPrice']),
);
$pdf->Row($row_data);
$totalPay += floatval($d['TotalPrice']);
$no++;
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 5), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[5][1], 7, $this->_fmt_rp($totalPay), 1, 0, 'R', true);
$pdf->Ln(8);
}
// =========================================================================
// PDF SECTION: Inspeksi & Verifikasi Jasa (Blok Terpisah)
// =========================================================================
private function _pdf_inspeksi($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
// Cek apakah ada data inspeksi yang terisi
$hasInspeksi = false;
foreach ($details as $d) {
if (!empty($d['HasilPengerjaan']) || !empty($d['Kesimpulan']) || !empty($d['Pekerja'])) {
$hasInspeksi = true;
break;
}
}
if (!$hasInspeksi) {
return;
}
$pdf->Ln(2);
$pdf->SetFont('Arial_Narrow', 'B', 10);
$pdf->Cell($pageW, 6, 'DETAIL HASIL INSPEKSI & VERIFIKASI JASA', 0, 1, 'L');
$pdf->SetLineWidth(0.3);
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(2);
$no = 1;
foreach ($details as $d) {
// Lewati jika item ini tidak memiliki data inspeksi sama sekali
if (empty($d['HasilPengerjaan']) && empty($d['Kesimpulan']) && empty($d['Pekerja'])) {
continue;
}
$pdf->SetFont('Arial_Narrow', 'B', 9);
$pdf->Cell($pageW, 5, $no . '. ' . ($d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-')), 0, 1, 'L');
// Indentasi isi detail inspeksi
$lblW = 32;
$valW = $pageW - $lblW - 4;
$items = array(
array('Petugas Pengerjaan', $d['Pekerja'] ?: '-'),
array('Staff Pemeriksa', $d['PemeriksaName'] ?: '-'),
array('Hasil Pengerjaan', $d['HasilPengerjaan'] ?: '-'),
array('Catatan Pengerjaan', $d['CatatanPengerjaan'] ?: '-'),
array('Kesimpulan Hasil', $d['Kesimpulan'] ?: '-'),
array('Catatan Pemeriksa', $d['CatatanKesimpulan'] ?: '-'),
);
foreach ($items as $item) {
$pdf->SetX(18); // Indent slightly to the right
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $item[1], 0, 'L');
}
$pdf->Ln(2);
$pdf->SetDrawColor(200, 200, 200);
$pdf->SetLineWidth(0.1);
$pdf->Line(15, $pdf->GetY(), 15 + $pageW, $pdf->GetY());
$pdf->Ln(2);
$pdf->SetDrawColor(0, 0, 0); // restore black
$no++;
}
}
// =========================================================================
// PDF SECTION: Syarat & Ketentuan
// =========================================================================
private function _pdf_terms()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$terms = array(
'Penerimaan pengerjaan jasa ini berdasarkan hasil verifikasi/inspeksi petugas di lapangan.',
'Tanda Terima Jasa (TTJ) ini sah untuk digunakan sebagai lampiran dokumen penagihan vendor.',
'Jika terdapat komplain/ketidaksesuaian hasil pengerjaan di kemudian hari, harap merujuk ke Berita Acara inspeksi.',
'Dokumen ini dicetak dan divalidasi secara otomatis melalui sistem ERP.'
);
$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, ',', '.');
}
// format jumlah/qty: tanpa desimal
private function _fmt_qty($n)
{
return number_format(floatval($n), 0, ',', '.');
}
private function _fmt_rp($n)
{
return 'Rp ' . number_format(floatval($n), 2, ',', '.');
}
}

View File

@@ -216,11 +216,10 @@ class Rpt_receive_transfer extends MY_Controller
$pdf->SetY($startY);
$leftItems = array(
array('Nomor SJ', $header['SuratJalanNumber'], true),
array('Tanggal SJ', $this->_fmt_date($header['SuratJalanDate']), false),
array('Nomor', $header['SuratJalanNumber'], true),
array('Tanggal', $this->_fmt_date($header['SuratJalanDate']), false),
array('Tanggal Terima', $this->_fmt_date($header['SuratJalanReceivedDate']), false),
array('Gudang Terima', $header['ReceivedWarehouseName'] ?: '-', false),
array('Status', $header['SuratJalanStatus'], false),
);
foreach ($leftItems as $item) {
@@ -289,15 +288,14 @@ class Rpt_receive_transfer extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 59 + 20 + 20 + 20 + 17 + 36 = 180
// 8 + 95 + 20 + 20 + 20 + 17 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama', 59, 'L'),
array('Nama', 95, 'L'),
array('Unit', 20, 'C'),
array('Qty Kirim', 20, 'R'),
array('Qty Terima', 20, 'R'),
array('Selisih', 17, 'R'),
array('No. PR / Ref', 36, 'L'),
);
// Header kolom
@@ -327,7 +325,6 @@ class Rpt_receive_transfer extends MY_Controller
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($qtyKirim), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_qty($qtyTerima), 1, 0, 'C');
$pdf->Cell($cols[5][1], 6, $this->_fmt_qty($selisih), 1, 0, 'C');
$pdf->Cell($cols[6][1], 6, $d['PurchaseRequestNumber'] ?: '-', 1, 0, 'L');
$pdf->Ln();
$no++;

View File

@@ -1,410 +0,0 @@
<?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 RequestPengeluaranBarangFpdf 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_request_pengeluaran_barang extends MY_Controller
{
// -- Properti bersama antar fungsi PDF -------------------------------------
/** @var RequestPengeluaranBarangFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
private $_username;
public function __construct()
{
parent::__construct();
}
public function index()
{
echo "Request Pengeluaran Barang Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (RequestItemOutID), username (opsional)
// =========================================================================
public function pdf()
{
try {
$id = intval($this->input->get_post('id'));
$username = trim($this->input->get_post('username') ?? '');
if ($id <= 0) {
$this->sys_error("ID tidak valid");
exit;
}
// -- Ambil data header & detail dari DB ----------------------------
$header = $this->_get_header($id);
$details = $this->_get_detail($id);
// -- Inisialisasi FPDF custom --------------------------------------
$this->_pdf = new RequestPengeluaranBarangFpdf('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->_header_data = $header;
$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($details);
$this->_pdf_terms();
// -- Output --------------------------------------------------------
$filename = 'RIO_' . str_replace('/', '-', $header['RequestItemOutNumber']) . '.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 header
// =========================================================================
private function _get_header($id)
{
$sql = "
SELECT
rio.*,
wh.WarehouseCode,
wh.WarehouseName,
wh.WarehouseType,
r.S_RegionalName,
d.DivisionCode,
d.DivisionName,
b.M_BranchName AS BranchName,
IFNULL(u.M_UserUsername, '') AS CreatedByName,
IFNULL(um.M_UserUsername, '') AS ManagerByName,
IFNULL(ua.M_UserUsername, '') AS ApprovedByName,
IFNULL(uv.M_UserUsername, '') AS VerifiedByName
FROM request_item_out rio
LEFT JOIN warehouse wh ON wh.WarehouseID = rio.RequestItemOutWarehouseID
AND wh.WarehouseIsActive = 'Y'
LEFT JOIN s_regional r ON r.S_RegionalID = rio.RequestItemOutS_RegionalID
AND r.S_RegionalIsActive = 'Y'
LEFT JOIN m_branch b ON b.M_BranchCode = rio.RequestItemOutM_BranchCode
AND b.M_BranchIsActive = 'Y'
LEFT JOIN division d ON d.DivisionID = rio.RequestItemOutDivisionID
AND d.DivisionIsActive = 'Y'
LEFT JOIN m_user u ON u.M_UserID = rio.RequestItemOutUserID
LEFT JOIN m_user um ON um.M_UserID = rio.RequestItemOutApprovedManagerUserID
LEFT JOIN m_user ua ON ua.M_UserID = rio.RequestItemOutApprovedUserID
LEFT JOIN m_user uv ON uv.M_UserID = rio.RequestItemOutVerifiedUserID
WHERE rio.RequestItemOutID = ?
AND rio.RequestItemOutIsActive = 'Y'
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Request Pengeluaran Barang tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
rod.*,
i.M_ItemCode,
i.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode
FROM request_item_out_detail rod
LEFT JOIN m_item i ON i.M_ItemID = rod.RequestItemOutDetailM_ItemID
LEFT JOIN itemunit iu ON iu.ItemUnitID = rod.RequestItemOutDetailItemUnitID
WHERE rod.RequestItemOutDetailRequestItemOutID = ?
AND rod.RequestItemOutDetailIsActive = 'Y'
ORDER BY rod.RequestItemOutDetailID ASC
";
$qry = $this->db->query($sql, array($id));
return $qry ? $qry->result_array() : array();
}
// =========================================================================
// PDF SECTION: Header — Judul + Informasi Dokumen (2 kolom)
// =========================================================================
private function _pdf_header()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$header = $this->_header_data;
// -- Judul utama --------------------------------------------------------
$pdf->SetFont('Arial_Narrow', 'B', 14);
$pdf->Cell($pageW, 8, 'REQUEST PENGELUARAN BARANG', 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 = 32;
$valW = $halfW - $lblW - 4;
// -- Kolom Kiri -----------------------------------------------------------
$pdf->SetY($startY);
$leftItems = array(
array('Nomor Request', $header['RequestItemOutNumber'], true),
array('Tanggal', $this->_fmt_date($header['RequestItemOutDate']), false),
array('Status', $header['RequestItemOutStatus'], false),
array('Divisi', $header['DivisionName'] ?: '-', false),
array('Gudang', $header['WarehouseName'] ?: '-', false),
array('Regional', $header['S_RegionalName'] ?: '-', false),
);
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');
}
// Keterangan (Note)
if (!empty($header['RequestItemOutNote'])) {
$pdf->SetX(15);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Keterangan', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->MultiCell($valW, 5, $header['RequestItemOutNote'], 0, 'L');
}
$leftY = $pdf->GetY();
// -- Kolom Kanan ----------------------------------------------------------
$pdf->SetY($startY);
$rightX = 15 + $halfW;
$rightItems = array(
array('Cabang', $header['BranchName'] ?: '-'),
array('Dibuat Oleh', $header['CreatedByName'] ?: '-'),
array('Dibuat Tgl', $this->_fmt_datetime($header['RequestItemOutCreated'])),
array('Approve Manager', $header['ManagerByName'] ?: '-'),
array('Tgl Appr. Manager',$header['RequestItemOutApprovedManagerDate'] ? $this->_fmt_datetime($header['RequestItemOutApprovedManagerDate']) : '-'),
array('Disetujui Oleh', $header['ApprovedByName'] ?: '-'),
array('Tgl Disetujui', $header['RequestItemOutApprovedDate'] ? $this->_fmt_datetime($header['RequestItemOutApprovedDate']) : '-'),
);
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');
}
// Verifikasi (hanya tampil jika ada)
if (!empty($header['RequestItemOutVerifiedDate'])) {
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Diverifikasi Oleh', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $header['VerifiedByName'] ?: '-', 0, 1, 'L');
$pdf->SetX($rightX);
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, 'Tgl Verifikasi', 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($valW, 5, $this->_fmt_datetime($header['RequestItemOutVerifiedDate']), 0, 1, 'L');
}
$rightY = $pdf->GetY();
// Posisikan Y ke yang paling bawah + margin
$pdf->SetY(max($leftY, $rightY) + 4);
}
// =========================================================================
// PDF SECTION: Data — Tabel detail item
// =========================================================================
private function _pdf_data($details)
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
// -- Definisi kolom: [label, lebar, align] -------------------------------
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 72 + 20 + 25 + 25 + 30 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama Item', 72, 'L'),
array('Unit', 20, 'C'),
array('Qty Request', 25, 'R'),
array('Qty Diterima', 25, 'R'),
array('Status', 30, 'C'),
);
// Header kolom
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->SetDrawColor(0, 0, 0);
foreach ($cols as $c) {
$pdf->Cell($c[1], 7, $c[0], 1, 0, 'C', true);
}
$pdf->Ln();
// Baris data
$pdf->SetLineWidth(0.2);
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
$total_qty = 0;
$total_recv = 0;
foreach ($details as $d) {
$qty = floatval($d['RequestItemOutDetailQty'] ?? 0);
$recvQty = floatval($d['RequestItemOutDetailReceiveQty'] ?? 0);
$status = $d['RequestItemOutDetailStatus'] ?? '-';
$pdf->Cell($cols[0][1], 6, $no, 1, 0, 'C');
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($qty), 1, 0, 'R');
$pdf->Cell($cols[4][1], 6, $this->_fmt_qty($recvQty), 1, 0, 'R');
$pdf->Cell($cols[5][1], 6, $status, 1, 0, 'C');
$pdf->Ln();
$total_qty += $qty;
$total_recv += $recvQty;
$no++;
}
// Baris TOTAL
$span = array_sum(array_column(array_slice($cols, 0, 3), 1));
$pdf->SetLineWidth(0.3);
$pdf->SetFont('Arial_Narrow', 'B', 8);
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($span, 7, 'TOTAL', 1, 0, 'R', true);
$pdf->Cell($cols[3][1], 7, $this->_fmt_qty($total_qty), 1, 0, 'R', true);
$pdf->Cell($cols[4][1], 7, $this->_fmt_qty($total_recv), 1, 0, 'R', true);
$pdf->Cell($cols[5][1], 7, '', 1, 0, 'C', true);
$pdf->Ln();
$pdf->Ln(4);
}
// =========================================================================
// PDF SECTION: Syarat & Ketentuan
// =========================================================================
private function _pdf_terms()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$terms = array(
'Request ini harus disetujui oleh atasan (manager) sebelum diproses lebih lanjut.',
'Barang yang diminta akan dikeluarkan sesuai dengan jumlah yang telah disetujui.',
'Apabila jumlah barang tidak tersedia, maka akan dilakukan pengeluaran sebagian (partial).',
'Dokumen ini bersifat internal dan hanya digunakan untuk keperluan administrasi perusahaan.',
);
$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, ',', '.');
}
}

View File

@@ -197,7 +197,7 @@ class Rpt_stock_request extends MY_Controller
array('Nomor', $header['PurchaseRequestNumber'], true),
array('Ref Nomor', $header['PurchaseRequestRefNumber'] ?: '-', false),
array('Tanggal', $this->_fmt_date($header['PurchaseRequestDate']), false),
array('Status', $header['PurchaseRequestStatus'], false),
array('Tanggal Approved', $this->_fmt_datetime($header['PurchaseRequestApprovedDate'])),
);
foreach ($leftItems as $item) {
@@ -205,7 +205,7 @@ class Rpt_stock_request extends MY_Controller
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
if ($item[2]) {
if (isset($item[2]) && $item[2]) {
$pdf->SetFont('Arial_Narrow', 'B', 9);
} else {
$pdf->SetFont('Arial_Narrow', '', 9);
@@ -233,7 +233,6 @@ class Rpt_stock_request extends MY_Controller
array('Kategori', $header['itemCategoryName'] ?: '-'),
array('Dibuat Oleh', $header['CreatedByName']),
array('Disetujui Oleh', $header['ApprovedByName'] ?: '-'),
array('Tanggal Approved', $this->_fmt_datetime($header['PurchaseRequestApprovedDate'])),
);
foreach ($rightItems as $item) {

View File

@@ -204,7 +204,7 @@ class Rpt_stock_request_np extends MY_Controller
array('Nomor', $header['PurchaseRequestNumber'], true),
array('Ref Nomor', $header['PurchaseRequestRefNumber'] ?: '-', false),
array('Tanggal', $this->_fmt_date($header['PurchaseRequestDate']), false),
array('Status', $header['PurchaseRequestStatus'], false),
array('Tanggal Approved', $this->_fmt_datetime($header['PurchaseRequestApprovedDate'])),
);
foreach ($leftItems as $item) {
@@ -212,7 +212,7 @@ class Rpt_stock_request_np extends MY_Controller
$pdf->SetFont('Arial_Narrow', '', 9);
$pdf->Cell($lblW, 5, $item[0], 0, 0, 'L');
$pdf->Cell(4, 5, ':', 0, 0, 'C');
if ($item[2]) {
if (isset($item[2]) && $item[2]) {
$pdf->SetFont('Arial_Narrow', 'B', 9);
} else {
$pdf->SetFont('Arial_Narrow', '', 9);
@@ -240,7 +240,6 @@ class Rpt_stock_request_np extends MY_Controller
array('Kategori', $header['itemCategoryName'] ?: '-'),
array('Dibuat Oleh', $header['CreatedByName']),
array('Disetujui Oleh', $header['ApprovedByName'] ?: '-'),
array('Tanggal Approved', $this->_fmt_datetime($header['PurchaseRequestApprovedDate'])),
);
foreach ($rightItems as $item) {

View File

@@ -211,10 +211,9 @@ class Rpt_surat_jalan extends MY_Controller
$pdf->SetY($startY);
$leftItems = array(
array('Nomor SJ', $header['SuratJalanNumber'], true),
array('Tanggal SJ', $this->_fmt_date($header['SuratJalanDate']), false),
array('Nomor', $header['SuratJalanNumber'], true),
array('Tanggal', $this->_fmt_date($header['SuratJalanDate']), false),
array('No. Transfer', $header['SuratJalanT_GoodsTransferNum'] ?: '-', false),
array('Status', $header['SuratJalanStatus'], false),
);
foreach ($leftItems as $item) {
@@ -283,13 +282,12 @@ class Rpt_surat_jalan extends MY_Controller
// ── Definisi kolom: [label, lebar, align] ─────────────────────────────
// Total lebar = 180mm (A4 portrait: 210 - margin 15 kiri - 15 kanan)
// 8 + 62 + 20 + 20 + 42 + 28 = 180
// 8 + 104 + 20 + 20 + 28 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama', 62, 'L'),
array('Nama', 104, 'L'),
array('Unit', 20, 'C'),
array('Qty Kirim', 20, 'R'),
array('No. PR / Ref', 42, 'L'),
array('Qty Diterima', 28, 'R'),
);
@@ -314,8 +312,7 @@ class Rpt_surat_jalan extends MY_Controller
$pdf->Cell($cols[1][1], 6, $d['M_ItemDesc'] ?: ($d['M_ItemCode'] ?: '-'), 1, 0, 'L');
$pdf->Cell($cols[2][1], 6, $d['ItemUnitName'] ?: ($d['ItemUnitCode'] ?: '-'), 1, 0, 'C');
$pdf->Cell($cols[3][1], 6, $this->_fmt_qty($d['SuratJalanDetailQty']), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $d['PurchaseRequestNumber'] ?: '-', 1, 0, 'L');
$pdf->Cell($cols[5][1], 6, $this->_fmt_qty($d['SuratJalanDetailQtyReceived']), 1, 0, 'C');
$pdf->Cell($cols[4][1], 6, $this->_fmt_qty($d['SuratJalanDetailQtyReceived']), 1, 0, 'C');
$pdf->Ln();
$no++;