Files
be-accone/application/controllers/report/Rpt_surat_jalan.php
2026-07-16 10:12:30 +07:00

386 lines
16 KiB
PHP

<?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
// Footer tidak bisa bergeser karena FPDF memanggil Footer() sebelum ganti hal.
// =============================================================================
class SuratJalanFpdf extends FPDF
{
public $printUsername = '-';
public $printDate = '';
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 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_surat_jalan extends MY_Controller
{
// ── Properti bersama antar fungsi PDF ─────────────────────────────────────
/** @var SuratJalanFpdf */
private $_pdf;
private $_pageW;
private $_header_data;
private $_username;
public function __construct()
{
parent::__construct();
}
public function index()
{
echo "Surat Jalan Report API";
}
// =========================================================================
// ENDPOINT: pdf
// GET/POST: id (SuratJalanID), 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 SuratJalanFpdf('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 = 'SJ_' . str_replace('/', '-', $header['SuratJalanNumber']) . '.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
sj.*,
ex.ExpeditionName,
ex.ExpeditionIsInternal,
exs.ExpeditionStaffName,
exs.ExpeditionStaffMobile,
tg.T_GoodsTransferNum,
tg.T_GoodsTransferDate,
tg.T_GoodsTransferType,
tg.T_GoodsTransferFromM_BranchCode,
tg.T_GoodsTransferToM_BranchCode,
bFrom.M_BranchName AS BranchFromName,
bFrom.M_BranchAddress AS BranchFromAddress,
bTo.M_BranchName AS BranchToName,
bTo.M_BranchAddress AS BranchToAddress
FROM suratjalan sj
LEFT JOIN t_goods_transfer tg ON tg.T_GoodsTransferID = sj.SuratJalanT_GoodsTransferID
AND tg.T_GoodsTransferIsActive = 'Y'
LEFT JOIN expedition ex ON ex.ExpeditionID = sj.SuratJalanEkspedisiID
AND ex.ExpeditionIsActive = 'Y'
LEFT JOIN expeditionstaff exs ON exs.ExpeditionStaffID = sj.SuratJalanEkspedisiStaffID
AND exs.ExpeditionStaffIsActive = 'Y'
LEFT JOIN m_branch bFrom ON bFrom.M_BranchCode = tg.T_GoodsTransferFromM_BranchCode
LEFT JOIN m_branch bTo ON bTo.M_BranchCode = tg.T_GoodsTransferToM_BranchCode
WHERE sj.SuratJalanIsActive = 'Y'
AND sj.SuratJalanID = ?
LIMIT 1
";
$qry = $this->db->query($sql, array($id));
if (!$qry || $qry->num_rows() === 0) {
$this->sys_error("Data Surat Jalan tidak ditemukan");
exit;
}
return $qry->row_array();
}
// =========================================================================
// DATABASE: Ambil data detail
// =========================================================================
private function _get_detail($id)
{
$sql = "
SELECT
sjd.*,
item.M_ItemCode,
item.M_ItemDesc,
iu.ItemUnitName,
iu.ItemUnitCode,
pr.PurchaseRequestNumber
FROM suratjalan_detail sjd
JOIN m_item item ON item.M_ItemID = sjd.SuratJalanDetailM_ItemID
AND item.M_ItemIsActive = 'Y'
LEFT JOIN itemunit iu ON iu.ItemUnitID = sjd.SuratJalanDetailItemUnitID
AND iu.ItemUnitIsActive = 'Y'
LEFT JOIN purchase_request_flag prf ON prf.PurchaseRequestFlagID = sjd.SuratJalanDetailPurchaseRequestFlagID
AND prf.PurchaseRequestFlagIsActive = 'Y'
LEFT JOIN purchase_request_detail prd ON prd.PurchaseRequestDetailID = prf.PurchaseRequestFlagPurchaseRequestDetailID
LEFT JOIN purchase_request pr ON pr.PurchaseRequestID = prd.PurchaseRequestDetailPurchaseRequestID
WHERE sjd.SuratJalanDetailIsActive = 'Y'
AND sjd.SuratJalanDetailSuratJalanID = ?
ORDER BY sjd.SuratJalanDetailID 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, 'SURAT JALAN (DELIVERY ORDER)', 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);
$leftItems = array(
array('Nomor SJ', $header['SuratJalanNumber'], true),
array('Tanggal SJ', $this->_fmt_date($header['SuratJalanDate']), false),
array('No. Transfer', $header['SuratJalanT_GoodsTransferNum'] ?: '-', false),
array('Status', $header['SuratJalanStatus'], 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 Status (tanpa spasi vertikal)
if (!empty($header['SuratJalanNote'])) {
$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['SuratJalanNote'], 0, 'L');
}
$leftY = $pdf->GetY();
// ── Kolom Kanan ───────────────────────────────────────────────────────
$pdf->SetY($startY);
$rightX = 15 + $halfW;
$rightItems = array(
array('Dari Cabang', $header['BranchFromName'] ?: '-'),
array('Alamat Asal', $header['BranchFromAddress'] ?: '-'),
array('Tujuan Cabang', $header['BranchToName'] ?: '-'),
array('Alamat Tujuan', $header['BranchToAddress'] ?: '-'),
array('Ekspedisi', $header['ExpeditionName'] ?: '-'),
array('Driver / Staf', $header['ExpeditionStaffName'] ?: '-'),
array('Kontak Driver', $header['SuratJalanEkspedisiStaffContact'] ?: '-'),
);
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 Asal' || $item[0] === 'Alamat Tujuan') {
$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 + 62 + 20 + 20 + 42 + 28 = 180
$cols = array(
array('No', 8, 'C'),
array('Nama / Uraian', 62, 'L'),
array('Unit', 20, 'C'),
array('Qty Kirim', 20, 'R'),
array('No. PR / Ref', 42, 'L'),
array('Qty Diterima', 28, '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();
// Baris data
$pdf->SetLineWidth(0.2); // border lebih tipis di data
$pdf->SetFont('Arial_Narrow', '', 8);
$pdf->SetFillColor(255, 255, 255);
$no = 1;
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['SuratJalanDetailQty']), 1, 0, 'R');
$pdf->Cell($cols[4][1], 6, $d['PurchaseRequestNumber'] ?: '-', 1, 0, 'L');
$pdf->Cell($cols[5][1], 6, $this->_fmt_qty($d['SuratJalanDetailQtyReceived']), 1, 0, 'R');
$pdf->Ln();
$no++;
}
$pdf->Ln(4);
}
// =========================================================================
// PDF SECTION: Syarat & Ketentuan
// =========================================================================
private function _pdf_terms()
{
$pdf = $this->_pdf;
$pageW = $this->_pageW;
$terms = array(
'Barang yang dikirimkan harus diperiksa kesesuaiannya dengan fisik sebelum ditandatangani oleh penerima.',
'Segala klaim kerusakan atau kekurangan barang setelah serah terima menjadi tanggung jawab penerima barang.',
'Surat jalan ini harus dikembalikan ke bagian logistik pengirim setelah ditandatangani oleh penerima.',
'Dokumen ini merupakan bukti sah serah terima barang antar cabang.'
);
$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, ',', '.');
}
}