Ekstrak Data Pajak dari PDF ke Excel Secara Otomatis dengan Python
Solusi otomatisasi untuk administrasi keuangan sekolah atau instansi
Bagi bendahara sekolah atau staf administrasi, mengolah data pajak dari file PDF BKU ke format Excel bisa sangat memakan waktu jika dilakukan manual. Solusinya, kita bisa otomatisasi proses ini menggunakan Python.
Tool ini akan membaca file PDF BKU, mengekstrak data pajak (PPh 21, PPh 23, PPN, dll), dan menyimpan ke Excel lengkap dengan filter, dropdown, conditional formatting, dan summary otomatis.
1. Persiapan & Instalasi Python
Pastikan Python sudah terpasang di komputer Anda. Cek di terminal/cmd:
python --versionJika belum ada, unduh dari python.org.
Install Library Tambahan
pip install pdfplumber pandas openpyxl tqdm- pdfplumber → membaca tabel dari PDF
- pandas → memproses data
- openpyxl → membuat & memformat Excel
- tqdm → progress bar saat proses
2. Cara Kerja Tool
- Mencari semua file PDF di folder
- Membaca data pajak dari setiap PDF
- Menyimpan hasil ke file Excel dengan fitur:
- Dropdown ✅❌ untuk status pembayaran
- Tanggal otomatis saat pilih sudah dibayar
- Warna hijau untuk sudah dibayar, kuning untuk terlambat
- Summary otomatis: total pajak, sudah/belum dibayar, persentase
- Filter & sorting bawaan Excel
- Instruksi penggunaan langsung di sheet
3. Menjalankan Script
- Simpan script
ekstrak_pajak.pydi folder yang sama dengan file PDF. - Buka terminal di folder tersebut.
- Jalankan:
python ekstrak_pajak.py - Semua PDF diproses otomatis → Excel baru dibuat dengan nama
_pajak.xlsx.
import pdfplumber
import pandas as pd
import os
import glob
import openpyxl
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
from openpyxl.formatting.rule import FormulaRule
from datetime import datetime
import subprocess
# === 1. SMART PDF FILE SELECTION ===
def select_pdf_file():
"""Fungsi untuk memilih file PDF secara smart"""
# Cari semua file PDF di direktori current
pdf_files = glob.glob("*.pdf")
if not pdf_files:
print("❌ Tidak ada file PDF di folder ini!")
print(" Pastikan file PDF berada di folder yang sama dengan script")
return None
# Filter hanya file BKU (case insensitive)
bku_files = [f for f in pdf_files if 'bku' in f.lower()]
if bku_files:
print("📁 File BKU yang ditemukan:")
for i, file in enumerate(bku_files, 1):
file_size = os.path.getsize(file) / 1024 # KB
print(f" {i}. {file} ({file_size:.1f} KB)")
if len(bku_files) == 1:
choice = 1
print(f" ✅ Otomatis memilih: {bku_files[0]}")
else:
try:
choice = int(input(" Pilih nomor file (1-{}): ".format(len(bku_files))))
if choice < 1 or choice > len(bku_files):
raise ValueError
except (ValueError, IndexError):
print(" ⚠️ Pilihan tidak valid, menggunakan file pertama")
choice = 1
return bku_files[choice-1]
else:
print("📁 File PDF yang ditemukan:")
for i, file in enumerate(pdf_files, 1):
file_size = os.path.getsize(file) / 1024
print(f" {i}. {file} ({file_size:.1f} KB)")
try:
choice = int(input(" Pilih nomor file (1-{}): ".format(len(pdf_files))))
if choice < 1 or choice > len(pdf_files):
raise ValueError
return pdf_files[choice-1]
except (ValueError, IndexError):
print(" ⚠️ Pilihan tidak valid, menggunakan file pertama")
return pdf_files[0]
# === 2. VALIDATE PDF FILE ===
def validate_pdf(file_path):
"""Validasi bahwa file PDF bisa dibaca"""
try:
with pdfplumber.open(file_path) as pdf:
if len(pdf.pages) == 0:
return False, "File PDF kosong"
return True, "OK"
except Exception as e:
return False, f"Error membaca PDF: {str(e)}"
# === MAIN EXECUTION ===
print("=" * 50)
print("📋 BKU Pajak Extractor")
print("=" * 50)
# Pilih file PDF
pdf_file = select_pdf_file()
if not pdf_file:
exit()
# Validasi file
is_valid, message = validate_pdf(pdf_file)
if not is_valid:
print(f"❌ {message}")
exit()
print(f"✅ File dipilih: {pdf_file}")
print(f" Size: {os.path.getsize(pdf_file) / 1024:.1f} KB")
print(f" Dibaca: {datetime.now().strftime('%d/%m/%Y %H:%M')}")
print("-" * 50)
# === 3. EKSTRAK DATA DARI PDF ===
data = []
last_bukti = None
total_pages = 0
extracted_count = 0
try:
with pdfplumber.open(pdf_file) as pdf:
total_pages = len(pdf.pages)
print(f"📄 Membaca {total_pages} halaman...")
for page_num, page in enumerate(pdf.pages, 1):
print(f" Processing page {page_num}/{total_pages}", end="\r")
table = page.extract_table()
if table:
for row in table[1:]: # Skip header
if len(row) >= 8: # Pastikan ada 8 kolom
tanggal, kode_keg, kode_rek, no_bukti, uraian, penerimaan, pengeluaran, saldo = row[:8]
if no_bukti and no_bukti.strip():
last_bukti = no_bukti.strip()
if (uraian and "Terima" in uraian and
any(pajak in uraian for pajak in ["PPh 21", "PPh 23", "PPN", "PPh 4(2)", "PPh Final"])):
# Deteksi jenis pajak
if "PPh 21" in uraian:
jenis_pajak = "PPh 21"
elif "PPh 23" in uraian:
jenis_pajak = "PPh 23"
elif "PPN" in uraian:
jenis_pajak = "PPN"
elif "PPh 4(2)" in uraian or "PPh Final" in uraian:
jenis_pajak = "PPh Final"
else:
jenis_pajak = "Pajak Lainnya"
data.append({
"NO. BUKTI": last_bukti,
"URAIAN": uraian.strip(),
"JENIS PAJAK": jenis_pajak,
"PENERIMAAN": penerimaan.strip() if penerimaan else "0",
"SUDAH DIBAYAR": "❌",
"TANGGAL BAYAR": ""
})
extracted_count += 1
print(f"\n✅ Berhasil ekstrak {extracted_count} data pajak")
except Exception as e:
print(f"\n❌ Error saat membaca PDF: {e}")
exit()
# === 4. SIMPAN KE EXCEL ===
if not data:
print("❌ Tidak ada data pajak yang ditemukan dalam file PDF!")
print(" Pastikan format BKU sesuai dan mengandung kata 'Terima' dengan jenis pajak")
exit()
nama_file = os.path.splitext(pdf_file)[0]
excel_file = f"{nama_file}_pajak.xlsx"
df = pd.DataFrame(data)
df["PENERIMAAN"] = df["PENERIMAAN"].str.replace(".", "", regex=False).astype(float)
df.to_excel(excel_file, index=False)
# === 5. ENHANCED EXCEL FORMATTING ===
wb = openpyxl.load_workbook(excel_file)
ws = wb.active
# Header untuk kolom baru
ws['E1'] = "SUDAH DIBAYAR"
ws['F1'] = "TANGGAL BAYAR"
# Dropdown validation
dv = DataValidation(type="list", formula1='"❌,✅"', allow_blank=True)
dv.add(f"E2:E{len(data)+1}")
ws.add_data_validation(dv)
# Data validation untuk tanggal
dv_date = DataValidation(type="date", operator="between",
formula1="DATE(2020,1,1)",
formula2=f"DATE({datetime.now().year+1},12,31)")
dv_date.add(f"F2:F{len(data)+1}")
ws.add_data_validation(dv_date)
# Set default values dan formula untuk tanggal otomatis
for row in range(2, len(data) + 2):
ws[f'E{row}'] = "❌"
ws[f'F{row}'].number_format = 'DD/MM/YYYY'
# Tambahkan formula: jika status ✅, isi tanggal hari ini
ws[f'F{row}'] = f'=IF(E{row}="✅", TODAY(), "")'
# Conditional formatting
green_fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
yellow_fill = PatternFill(start_color="FFEB9C", end_color="FFEB9C", fill_type="solid")
for row in range(2, len(data) + 2):
# Hijau untuk sudah dibayar
rule_green = FormulaRule(formula=[f'$E{row}="✅"'], fill=green_fill)
ws.conditional_formatting.add(f'A{row}:F{row}', rule_green)
# Kuning untuk belum dibayar tapi sudah lewat 30 hari dari sekarang
rule_yellow = FormulaRule(formula=[f'AND($E{row}="❌", F{row}<>"", TODAY()-F{row}>30)'], fill=yellow_fill)
ws.conditional_formatting.add(f'A{row}:F{row}', rule_yellow)
# Style header
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
header_font = Font(color="FFFFFF", bold=True)
thin_border = Border(left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin'))
for col in range(1, 7): # A sampai F
cell = ws.cell(row=1, column=col)
cell.fill = header_fill
cell.font = header_font
cell.border = thin_border
cell.alignment = Alignment(horizontal="center", vertical="center")
# Auto filter
ws.auto_filter.ref = ws.dimensions
# Column widths
widths = [15, 50, 15, 15, 15, 15]
for col, width in enumerate(widths, 1):
ws.column_dimensions[openpyxl.utils.get_column_letter(col)].width = width
# Freeze panes
ws.freeze_panes = "A2"
# Add summary table
ws['H1'] = "SUMMARY STATUS"
ws['H2'] = "Total Pajak"
ws['H3'] = "Sudah Dibayar"
ws['H4'] = "Belum Dibayar"
ws['H5'] = "Persentase"
ws['H6'] = "Terakhir Update"
ws['I1'] = "NILAI"
ws['I2'] = f"=SUM(D2:D{len(data)+1})"
ws['I3'] = f"=SUMIF(E2:E{len(data)+1},\"✅\",D2:D{len(data)+1})"
ws['I4'] = f"=SUMIF(E2:E{len(data)+1},\"❌\",D2:D{len(data)+1})"
ws['I5'] = f"=I3/I2"
ws['I6'] = f'=NOW()'
# Format summary
for row in range(2, 6):
ws[f'I{row}'].number_format = '#,##0'
ws['I5'].number_format = '0.00%'
ws['I6'].number_format = 'DD/MM/YYYY HH:MM'
# Format currency untuk PENERIMAAN
for row in range(2, len(data) + 2):
ws[f'D{row}'].number_format = '#,##0'
# Tambahkan instruksi penggunaan
ws['K1'] = "📋 INSTRUKSI PENGGUNAAN"
ws['K2'] = "1. Pilih '✅' di kolom E untuk menandai sudah dibayar"
ws['K3'] = "2. Tanggal akan terisi otomatis dengan hari ini"
ws['K4'] = "3. Untuk ubah tanggal, edit manual di kolom F"
ws['K5'] = "4. Filter dengan dropdown di header kolom"
ws['K6'] = "5. Lihat summary otomatis di kolom H-I"
# Auto width untuk kolom instruksi
ws.column_dimensions['K'].width = 35
wb.save(excel_file)
# === 6. AUTO OPEN FILE ===
excel_path = os.path.abspath(excel_file)
if os.path.exists(excel_path):
print(f"🔗 Membuka file Excel...")
try:
os.startfile(excel_path)
print(f"✅ File Excel berhasil dibuka!")
except:
print(f"❌ Gagal membuka file secara otomatis")
print(f" Buka manual: {excel_path}")
else:
print(f"❌ File tidak ditemukan: {excel_path}")
print(f"""\n🎉 SUKSES! File '{excel_file}' telah dibuat dengan:
📊 Statistik:
• File sumber: {pdf_file}
• Halaman diproses: {total_pages}
• Data pajak ditemukan: {extracted_count}
• Jenis pajak: {', '.join(df['JENIS PAJAK'].unique())}
💡 Fitur:
• Tanggal otomatis ketika pilih ✅
• Summary lengkap dengan total: Rp {df['PENERIMAAN'].sum():,.0f}
• Filter, sorting, dan conditional formatting
• Format professional siap pakai
📍 File disimpan di: {excel_path}
""")
4. Fitur di File Excel
- Kolom E (SUDAH DIBAYAR): Pilih ✅ → baris hijau otomatis.
- Kolom F (TANGGAL BAYAR): Tanggal terisi otomatis, bisa ubah manual.
- Summary di Kolom H-I: Total pajak, sudah/belum dibayar, persentase.
- Filter di Header: Sortir & filter data dengan mudah.
5. Contoh Output
| NO. BUKTI | URAIAN | JENIS PAJAK | PENERIMAAN | SUDAH DIBAYAR | TANGGAL BAYAR |
|---|---|---|---|---|---|
| BPU128 | Terima PPh 23 … | PPh 23 | 40.000 | ❌ | |
| BPU143 | Terima PPh 21 … | PPh 21 | 234.000 | ❌ |
6. Penutup
Dengan script ini, pekerjaan mengolah data pajak dari PDF ke Excel jadi jauh lebih cepat, akurat, dan rapi. Tidak perlu copy-paste manual, semua otomatis: dari ekstraksi data sampai summary laporan.
No Comments yet!