513 lines
19 KiB
PHP
513 lines
19 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
|
|
// =============================================================================
|
|
class PaymentVoucherFpdf 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');
|
|
}
|
|
|
|
// 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_payment_voucher extends MY_Controller
|
|
{
|
|
// ── Properti bersama antar fungsi PDF ─────────────────────────────────────
|
|
/** @var PaymentVoucherFpdf */
|
|
private $_pdf;
|
|
private $_pageW;
|
|
private $_header_data;
|
|
private $_username;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
echo "Payment Voucher Report API";
|
|
}
|
|
|
|
// =========================================================================
|
|
// ENDPOINT: pdf
|
|
// GET/POST: id (PaymentVoucherID), 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 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;
|
|
$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 = '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');
|
|
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
|
|
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 Payment Voucher tidak ditemukan");
|
|
exit;
|
|
}
|
|
return $qry->row_array();
|
|
}
|
|
|
|
private function _get_detail($id)
|
|
{
|
|
$sql = "
|
|
SELECT
|
|
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();
|
|
}
|
|
|
|
// =========================================================================
|
|
// 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, '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());
|
|
$pdf->Ln(2);
|
|
|
|
$startY = $pdf->GetY();
|
|
$halfW = $pageW / 2;
|
|
$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['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);
|
|
if ($item[2]) {
|
|
$pdf->SetFont('Arial_Narrow', 'B', 9);
|
|
} else {
|
|
$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);
|
|
}
|
|
|
|
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 / 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, 'Catatan Bayar', 0, 0, 'L');
|
|
$pdf->Cell(4, 5, ':', 0, 0, 'C');
|
|
$pdf->MultiCell($valW, 5, $header['PaymentVoucherPaidNote'], 0, 'L');
|
|
}
|
|
$leftY = $pdf->GetY();
|
|
|
|
// ── Kolom Kanan ───────────────────────────────────────────────────────
|
|
$pdf->SetY($startY);
|
|
$rightX = 15 + $halfW;
|
|
|
|
$rightItems = array(
|
|
array('Cabang', $header['M_BranchName'] ?: '-'),
|
|
array('Tgl Pembayaran', $this->_fmt_date($header['PaymentVoucherPaidDate'])),
|
|
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
|
|
// =========================================================================
|
|
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 + 45 + 30 + 72 + 25 = 180
|
|
$cols = array(
|
|
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); // 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) {
|
|
$amount = floatval($d['PurchaseRequestDirectDetailTotalRealitationPrice']);
|
|
if ($amount <= 0) {
|
|
$amount = floatval($d['PurchaseRequestDirectDetailTotalEstimationPrice']);
|
|
}
|
|
|
|
$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, 4), 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 PEMBAYARAN', 1, 0, 'R', true);
|
|
$pdf->Cell($cols[4][1], 7, $this->_fmt_rp($totalPay), 1, 0, 'R', true);
|
|
$pdf->Ln(6);
|
|
|
|
// Ringkasan Biaya di Kanan Bawah (sejajar dengan Nominal Bayar)
|
|
$pdf->Ln(2);
|
|
}
|
|
|
|
// =========================================================================
|
|
// PDF SECTION: Syarat & Ketentuan
|
|
// =========================================================================
|
|
private function _pdf_terms()
|
|
{
|
|
$pdf = $this->_pdf;
|
|
$pageW = $this->_pageW;
|
|
|
|
$terms = array(
|
|
'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);
|
|
$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, ',', '.');
|
|
}
|
|
}
|