$(document).ready(function () { $(".btn-export-pdf").on("click", async function () { const data = window.ReportApp?.lastResponse; if (!data) { alert("Data belum tersedia"); return; } printPDF(data); }); }); function printPDF(data) { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: "portrait", // portrait kesempitan buat 16 kolom unit: "mm", format: "a4", }); console.log(data.vehicle.date); const pageWidth = doc.internal.pageSize.getWidth(); // ===== HEADER ===== doc.setFont("helvetica", "bold"); doc.setFontSize(12); doc.text("MENTENG SQUARE - RAYA PARKING - RAYA Parking", 14, 12); doc.setFontSize(11); doc.text("Rekap Transaksi Per Kendaraan", pageWidth / 2, 18, { align: "center" }); doc.setFont("helvetica", "normal"); doc.setFontSize(9); doc.text("Tanggal : " + (data.vehicle.date ?? "-"), pageWidth / 2, 23, { align: "center" }); doc.text("By : " + (data.vehicle.reportTime ?? "-"), pageWidth - 14, 12, { align: "right" }); // doc.text("By : Harian", pageWidth - 14, 12, { align: "right" }); // ===== SUSUN BODY TABEL ===== const vehicleBody = []; let totalTunaiJml = 0, totalTunaiNilai = 0; let totalNonTunaiJml = 0, totalNonTunaiNilai = 0; let grandTotal = 0; data.vehicle.tunai.forEach(function (cash) { const cashless = data.vehicle.nontunai.find((nt) => String(nt.id_kendaraan) === String(cash.id_kendaraan)) || { nonTunaiOut: 0, keluar: { casual: { jumlah: 0, income: 0 }, lost: { jumlah: 0, income: 0 }, member: { jumlah: 0, income: 0 }, manual: { jumlah: 0, income: 0 }, }, totalCashless: 0, }; // debugger; const tCasual = cash.keluar.casual; const tLost = cash.keluar.lost; const tMember = cash.keluar.member; const tManual = cash.keluar.manual; const tMasalah = cash.keluar.masalah ?? { jumlah: 0, income: 0 }; const totalTunai = cash.totalCash || 0; const nCasual = cashless.keluar.casual; const nLost = cashless.keluar.lost; const nMember = cashless.keluar.member; const nManual = cashless.keluar.manual; const nMasalah = cashless.keluar.masalah ?? { jumlah: 0, income: 0 }; const totalNonTunai = cashless.totalCashless || 0; // baris TUNAI vehicleBody.push([ { content: cash.jenis_mobil, rowSpan: 2, styles: { valign: "middle" } }, { content: "Tunai", styles: { fillColor: [40, 167, 69], textColor: 255, fontStyle: "bold" } }, cash.total, cash.tunaiOut, tCasual.jumlah, rupiah(tCasual.income), tLost.jumlah, rupiah(tLost.income), tMember.jumlah, rupiah(tMember.income), tManual.jumlah, rupiah(tManual.income), 0, 0, tMasalah.jumlah, rupiah(tMasalah.income), { content: rupiah(cash.totalCash), styles: { valign: "middle", fontStyle: "bold" } }, ]); // baris NON TUNAI vehicleBody.push([ { content: "Non Tunai", styles: { fillColor: [255, 193, 7], textColor: 0, fontStyle: "bold" } }, cash.total, cashless.nonTunaiOut, nCasual.jumlah, rupiah(nCasual.income), nLost.jumlah, rupiah(nLost.income), nMember.jumlah, rupiah(nMember.income), nManual.jumlah, rupiah(nManual.income), 0, 0, nMasalah.jumlah, rupiah(nMasalah.income), { content: rupiah(cashless.totalCashless), styles: { valign: "middle", fontStyle: "bold" } }, ]); totalTunaiJml += tCasual.jumlah + tLost.jumlah + tMember.jumlah + tManual.jumlah + tMasalah.jumlah; totalTunaiNilai += cash.totalCash || 0; totalNonTunaiJml += nCasual.jumlah + nLost.jumlah + nMember.jumlah + nManual.jumlah + nMasalah.jumlah; totalNonTunaiNilai += cashless.totalCashless || 0; grandTotal += (cash.totalCash || 0) + (cashless.totalCashless || 0); }); // baris TOTAL & GRAND TOTAL vehicleBody.push([ { content: "TOTAL", rowSpan: 2, styles: { fontStyle: "bold" } }, { content: "Tunai", styles: { fillColor: [40, 167, 69], textColor: 255, fontStyle: "bold" } }, "", "", { content: totalTunaiJml, styles: { fontStyle: "bold" } }, rupiah(totalTunaiNilai), "", "", "", "", "", "", "", "", "", "", rupiah(totalTunaiNilai), ]); vehicleBody.push([ { content: "NON TUNAI", styles: { fillColor: [255, 193, 7], fontStyle: "bold" } }, "", "", { content: totalNonTunaiJml, styles: { fontStyle: "bold" } }, rupiah(totalNonTunaiNilai), "", "", "", "", "", "", "", "", "", "", rupiah(totalNonTunaiNilai), ]); vehicleBody.push([ { content: "GRAND TOTAL", colSpan: 4, styles: { fontStyle: "bold", halign: "center" } }, { content: totalTunaiJml + totalNonTunaiJml, styles: { fontStyle: "bold" } }, { content: rupiah(totalTunaiNilai + totalNonTunaiNilai), styles: { fontStyle: "bold" } }, "", "", "", "", "", "", "", "", "", "", "", "", { content: rupiah(grandTotal), styles: { fontStyle: "bold" } }, ]); // ===== HEADER 2 BARIS (grouping) ===== const head = [ [ { content: "Jenis Kend", rowSpan: 2, colSpan: 2 }, { content: "Jenis Transaksi", colSpan: 2 }, { content: "Casual", colSpan: 2 }, { content: "Tiket Hilang", colSpan: 2 }, { content: "Member", colSpan: 2 }, { content: "Manual Input", colSpan: 2 }, { content: "Manual Mix", colSpan: 2 }, { content: "Tiket Masalah", colSpan: 2 }, { content: "Pendapatan", rowSpan: 2 }, ], [ "Masuk", "Keluar", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", ], ]; doc.autoTable({ head: head, body: vehicleBody, startY: 28, theme: "grid", styles: { fontSize: 6, halign: "center", valign: "middle", lineColor: [200, 200, 200] }, headStyles: { fillColor: [230, 230, 230], textColor: 0, fontStyle: "bold" }, columnStyles: { 0: { halign: "left" } }, }); // ===== TABEL OVER NIGHT (di bawah tabel utama) ===== const onBody = data.vehicle.overNight.map((on) => [ on.jenis_mobil ?? "-", on.overNight, on.onExpired, (on.overNight || 0) + (on.onExpired || 0), ]); // doc.setFont("helvetica", "bold"); // doc.setFontSize(8); // doc.text("Kendaraan yang masih menginap (Over Night)", 14, doc.lastAutoTable.finalY + 10); // doc.autoTable({ // head: [["Jenis Kend", "Menginap", "ON (Expired)", "Total"]], // body: onBody, // startY: doc.lastAutoTable.finalY + 13, // theme: "grid", // styles: { fontSize: 7, cellPadding: 1 }, // headStyles: { fontSize: 8, fillColor: [230, 230, 230], textColor: 0 }, // tableWidth: 100, // }); //================================================================== //===================== Print table officer ======================== //================================================================== const officerStartY = doc.lastAutoTable.finalY + 15; const officerBody = []; let ptotalTunaiJml = 0, ptotalTunaiNilai = 0; let ptotalNonTunaiJml = 0, ptotalNonTunaiNilai = 0; let pgrandTotal = 0; data.officer.tunai.forEach(function (cash) { const cashless = data.officer.nontunai.find((nt) => String(nt.petugas) === String(cash.petugas)) || { nonTunaiOut: 0, keluar: { casual: { jumlah: 0, income: 0 }, lost: { jumlah: 0, income: 0 }, member: { jumlah: 0, income: 0 }, manual: { jumlah: 0, income: 0 }, }, totalCashless: 0, }; // debugger; const tCasual = cash.keluar.casual; const tLost = cash.keluar.lost; const tMember = cash.keluar.member; const tManual = cash.keluar.manual; const tMasalah = cash.keluar.masalah ?? { jumlah: 0, income: 0 }; const totalTunai = cash.totalCash || 0; const oCasual = cashless.keluar.casual; const nLost = cashless.keluar.lost; const nMember = cashless.keluar.member; const nManual = cashless.keluar.manual; const nMasalah = cashless.keluar.masalah ?? { jumlah: 0, income: 0 }; const totalNonTunai = cashless.totalCashless || 0; // baris TUNAI officerBody.push([ { content: cash.petugas?.trim() ? cash.petugas : "NO-OP", rowSpan: 2, styles: { valign: "middle" }, }, { content: "Tunai", styles: { fillColor: [40, 167, 69], textColor: 255, fontStyle: "bold" } }, cash.total, cash.tunaiOut, tCasual.jumlah, rupiah(tCasual.income), tLost.jumlah, rupiah(tLost.income), tMember.jumlah, rupiah(tMember.income), tManual.jumlah, rupiah(tManual.income), 0, 0, tMasalah.jumlah, rupiah(tMasalah.income), { content: rupiah(cash.totalCash), styles: { valign: "middle", fontStyle: "bold" } }, ]); // baris NON TUNAI officerBody.push([ { content: "Non Tunai", styles: { fillColor: [255, 193, 7], textColor: 0, fontStyle: "bold" } }, cash.total, cashless.nonTunaiOut, oCasual.jumlah, rupiah(oCasual.income), nLost.jumlah, cash.totalCashless, nMember.jumlah, rupiah(nMember.income), nManual.jumlah, rupiah(nManual.income), 0, 0, nMasalah.jumlah, rupiah(nMasalah.income), { content: rupiah(cashless.totalCashless), styles: { valign: "middle", fontStyle: "bold" } }, ]); ptotalTunaiJml += tCasual.jumlah + tLost.jumlah + tMember.jumlah + tManual.jumlah + tMasalah.jumlah; ptotalTunaiNilai += cash.totalCash || 0; ptotalNonTunaiJml += oCasual.jumlah + nLost.jumlah + nMember.jumlah + nManual.jumlah + nMasalah.jumlah; ptotalNonTunaiNilai += cashless.totalCashless || 0; pgrandTotal += (cash.totalCash || 0) + (cashless.totalCashless || 0); }); // baris TOTAL & GRAND TOTAL officerBody.push([ { content: "TOTAL", rowSpan: 2, styles: { fontStyle: "bold" } }, { content: "Tunai", styles: { fillColor: [40, 167, 69], textColor: 255, fontStyle: "bold" } }, "", "", { content: ptotalTunaiJml, styles: { fontStyle: "bold" } }, rupiah(ptotalTunaiNilai), "", "", "", "", "", "", "", "", "", "", rupiah(ptotalTunaiNilai), ]); officerBody.push([ { content: "NON TUNAI", styles: { fillColor: [255, 193, 7], fontStyle: "bold" } }, "", "", { content: ptotalNonTunaiJml, styles: { fontStyle: "bold" } }, rupiah(ptotalNonTunaiNilai), "", "", "", "", "", "", "", "", "", "", rupiah(ptotalNonTunaiNilai), ]); officerBody.push([ { content: "GRAND TOTAL", colSpan: 4, styles: { fontStyle: "bold", halign: "center" } }, { content: ptotalTunaiJml + ptotalNonTunaiJml, styles: { fontStyle: "bold" } }, { content: rupiah(ptotalTunaiNilai + ptotalNonTunaiNilai), styles: { fontStyle: "bold" } }, "", "", "", "", "", "", "", "", "", "", // "", // "", { content: rupiah(pgrandTotal), styles: { fontStyle: "bold" } }, ]); // ===== HEADER 2 BARIS (grouping) ===== const officerHead = [ [ { content: "Nama Petugas", rowSpan: 2, colSpan: 2 }, { content: "Jenis Transaksi", colSpan: 2 }, { content: "Casual", colSpan: 2 }, { content: "Tiket Hilang", colSpan: 2 }, { content: "Member", colSpan: 2 }, { content: "Manual Input", colSpan: 2 }, { content: "Manual Mix", colSpan: 2 }, { content: "Tiket Masalah", colSpan: 2 }, { content: "Pendapatan", rowSpan: 2 }, ], [ "Masuk", "Keluar", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", ], ]; // doc.addPage(); doc.setFont("helvetica", "bold"); doc.setFontSize(11); doc.text("Rekap Transaksi Per Petugas", pageWidth / 2, officerStartY - 6, { align: "center" }); doc.setFont("helvetica", "normal"); doc.setFontSize(9); doc.text("Tanggal : " + (data.vehicle.date ?? "-"), pageWidth / 2, officerStartY - 2, { align: "center" }); doc.text("By : " + (data.vehicle.reportTime ?? "-"), pageWidth - 14, officerStartY - 2, { align: "right" }); doc.autoTable({ head: officerHead, body: officerBody, startY: officerStartY, // lastAutoTable: finalY + 15, theme: "grid", styles: { fontSize: 6, halign: "center", valign: "middle", lineColor: [200, 200, 200] }, headStyles: { fillColor: [230, 230, 230], textColor: 0, fontStyle: "bold" }, columnStyles: { 0: { halign: "left" } }, }); //==================================== //======== Print table payment ======= //==================================== const paymentStartY = doc.lastAutoTable.finalY + 15; const paymentBody = []; let payTotalTunaiJml = 0, payTotalTunaiNilai = 0; let payTotalNonTunaiJml = 0, payTotalNonTunaiNilai = 0; let payGrandTotal = 0; data.payment.tunai.forEach(function (cash) { const cashless = data.payment.nontunai.find((nt) => String(nt.typePayment) === String(cash.typePayment)) || { nonTunaiOut: 0, keluar: { casual: { jumlah: 0, income: 0 }, lost: { jumlah: 0, income: 0 }, member: { jumlah: 0, income: 0 }, manual: { jumlah: 0, income: 0 }, }, totalCashless: 0, }; // debugger; let typePayment = paymentType(cash.typePayment); const tCasual = cash.keluar.casual; const tLost = cash.keluar.lost; const tMember = cash.keluar.member; const tManual = cash.keluar.manual; const tMasalah = cash.keluar.masalah ?? { jumlah: 0, income: 0 }; const totalTunai = cash.totalCash || 0; const oCasual = cashless.keluar.casual; const nLost = cashless.keluar.lost; const nMember = cashless.keluar.member; const nManual = cashless.keluar.manual; const nMasalah = cashless.keluar.masalah ?? { jumlah: 0, income: 0 }; const totalNonTunai = cashless.totalCashless || 0; // baris TUNAI paymentBody.push([ { content: typePayment, rowSpan: 2, styles: { valign: "middle" }, }, { content: "Tunai", styles: { fillColor: [40, 167, 69], textColor: 255, fontStyle: "bold" } }, cash.total, cash.tunaiOut, tCasual.jumlah, rupiah(tCasual.income), tLost.jumlah, rupiah(tLost.income), tMember.jumlah, rupiah(tMember.income), tManual.jumlah, rupiah(tManual.income), 0, 0, tMasalah.jumlah, rupiah(tMasalah.income), { content: rupiah(cash.totalCash), styles: { valign: "middle", fontStyle: "bold" } }, ]); // baris NON TUNAI paymentBody.push([ { content: "Non Tunai", styles: { fillColor: [255, 193, 7], textColor: 0, fontStyle: "bold" } }, cash.total, cashless.nonTunaiOut, oCasual.jumlah, rupiah(oCasual.income), nLost.jumlah, rupiah(nLost.income), nMember.jumlah, rupiah(nMember.income), nManual.jumlah, rupiah(nManual.income), 0, 0, nMasalah.jumlah, rupiah(nMasalah.income), { content: rupiah(cashless.totalCashless), styles: { valign: "middle", fontStyle: "bold" } }, ]); payTotalTunaiJml += tCasual.jumlah + tLost.jumlah + tMember.jumlah + tManual.jumlah + tMasalah.jumlah; payTotalTunaiNilai += cash.totalCash || 0; payTotalNonTunaiJml += oCasual.jumlah + nLost.jumlah + nMember.jumlah + nManual.jumlah + nMasalah.jumlah; payTotalNonTunaiNilai += cashless.totalCashless || 0; payGrandTotal += (cash.totalCash || 0) + (cashless.totalCashless || 0); }); // baris TOTAL & GRAND TOTAL paymentBody.push([ { content: "TOTAL", rowSpan: 2, styles: { fontStyle: "bold" } }, { content: "Tunai", styles: { fillColor: [40, 167, 69], textColor: 255, fontStyle: "bold" } }, "", "", { content: payTotalTunaiJml, styles: { fontStyle: "bold" } }, rupiah(payTotalTunaiNilai), "", "", "", "", "", "", "", "", "", "", rupiah(ptotalTunaiNilai), ]); paymentBody.push([ { content: "NON TUNAI", styles: { fillColor: [255, 193, 7], fontStyle: "bold" } }, "", "", { content: payTotalNonTunaiJml, styles: { fontStyle: "bold" } }, rupiah(payTotalNonTunaiNilai), "", "", "", "", "", "", "", "", "", "", rupiah(payTotalNonTunaiNilai), ]); paymentBody.push([ { content: "GRAND TOTAL", colSpan: 4, styles: { fontStyle: "bold", halign: "center" } }, { content: payTotalTunaiJml + payTotalNonTunaiJml, styles: { fontStyle: "bold" } }, { content: rupiah(payTotalTunaiNilai + payTotalNonTunaiNilai), styles: { fontStyle: "bold" } }, "", "", "", "", "", "", "", "", "", "", // "", // "", { content: rupiah(payGrandTotal), styles: { fontStyle: "bold" } }, ]); // ===== HEADER 2 BARIS (grouping) ===== const paymentHead = [ [ { content: "Jenis Pembayaran", rowSpan: 2, colSpan: 2 }, { content: "Jenis Transaksi", colSpan: 2 }, { content: "Casual", colSpan: 2 }, { content: "Tiket Hilang", colSpan: 2 }, { content: "Member", colSpan: 2 }, { content: "Manual Input", colSpan: 2 }, { content: "Manual Mix", colSpan: 2 }, { content: "Tiket Masalah", colSpan: 2 }, { content: "Pendapatan", rowSpan: 2 }, ], [ "Masuk", "Keluar", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", "JML", "Nilai", ], ]; // doc.addPage(); doc.setFont("helvetica", "bold"); doc.setFontSize(11); doc.text("Rekap Transaksi Per Pembayaran", pageWidth / 2, paymentStartY - 6, { align: "center" }); doc.setFont("helvetica", "normal"); doc.setFontSize(9); doc.text("Tanggal : " + (data.vehicle.date ?? "-"), pageWidth / 2, paymentStartY - 2, { align: "center" }); doc.text("By : " + (data.vehicle.reportTime ?? "-"), pageWidth - 14, paymentStartY - 2, { align: "right" }); doc.autoTable({ head: paymentHead, body: paymentBody, startY: paymentStartY, // lastAutoTable: finalY + 15, theme: "grid", styles: { fontSize: 6, halign: "center", valign: "middle", lineColor: [200, 200, 200] }, headStyles: { fillColor: [230, 230, 230], textColor: 0, fontStyle: "bold" }, columnStyles: { 0: { halign: "left" } }, }); doc.save(`Rekap Harian - ${data.vehicle.date}.pdf`); } function paymentType(value) { switch (value) { case 2: value = "Mandiri"; break; case 3: value = "BRIZZI"; break; case 4: value = "TapCash"; break; case 5: value = "Flazz"; break; case 9: value = "QRIS"; break; default: value = "Tunai"; } return value; }