grafik laporan penjualan
This commit is contained in:
parent
a2c374242f
commit
a645ee5d5b
BIN
lap/tes3.png
Normal file
BIN
lap/tes3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
BIN
lap/tes4.png
Normal file
BIN
lap/tes4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
BIN
lap/tes5.png
Normal file
BIN
lap/tes5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
138
main.py
138
main.py
@ -3316,7 +3316,14 @@ class App:
|
|||||||
text="💾 Export Laporan",
|
text="💾 Export Laporan",
|
||||||
command=self.export_report_to_file,
|
command=self.export_report_to_file,
|
||||||
style="Accent.TButton"
|
style="Accent.TButton"
|
||||||
).grid(row=3, column=0, pady=10)
|
).grid(row=3, column=0, pady=(10, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
filter_frame,
|
||||||
|
text="🖼️ Export Grafik (PNG)",
|
||||||
|
command=self.export_report_chart,
|
||||||
|
style="Accent.TButton"
|
||||||
|
).grid(row=4, column=0, pady=5)
|
||||||
|
|
||||||
# Summary frame
|
# Summary frame
|
||||||
summary_frame = ttk.LabelFrame(parent, text="📈 Ringkasan", padding=10)
|
summary_frame = ttk.LabelFrame(parent, text="📈 Ringkasan", padding=10)
|
||||||
@ -3851,6 +3858,135 @@ class App:
|
|||||||
# Fallback: Tampilkan grafik ASCII sederhana
|
# Fallback: Tampilkan grafik ASCII sederhana
|
||||||
self.show_text_chart()
|
self.show_text_chart()
|
||||||
|
|
||||||
|
def export_report_chart(self):
|
||||||
|
"""Export grafik laporan ke file gambar (PNG) - REVISI FREKUENSI TRANSAKSI"""
|
||||||
|
try:
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.ticker import MaxNLocator # Untuk sumbu Y bilangan bulat
|
||||||
|
from datetime import datetime
|
||||||
|
except ImportError:
|
||||||
|
messagebox.showerror("Error", "Modul 'matplotlib' belum terinstall.\nSilakan install dengan: pip install matplotlib")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 1. Cek Data
|
||||||
|
if not self.report_tree.get_children():
|
||||||
|
messagebox.showwarning("Data Kosong", "Generate laporan terlebih dahulu sebelum export grafik.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ambil periode saat ini ('harian', 'mingguan', atau 'bulanan')
|
||||||
|
periode_pilihan = self.report_period_var.get()
|
||||||
|
|
||||||
|
# 2. Kumpulkan Data dari Treeview
|
||||||
|
metode_counts = {}
|
||||||
|
trend_data = {} # Bisa per jam atau per hari tergantung periode
|
||||||
|
|
||||||
|
for item_id in self.report_tree.get_children():
|
||||||
|
values = self.report_tree.item(item_id)['values']
|
||||||
|
# Format values: (tid, tanggal, meja, total_str, metode, status)
|
||||||
|
tanggal_str = values[1] # Format: DD/MM/YYYY HH:MM
|
||||||
|
metode = values[4]
|
||||||
|
|
||||||
|
# Hitung Pie Chart (Metode)
|
||||||
|
metode_counts[metode] = metode_counts.get(metode, 0) + 1
|
||||||
|
|
||||||
|
# Hitung Bar Chart (Trend Transaksi)
|
||||||
|
try:
|
||||||
|
dt_obj = datetime.strptime(tanggal_str, "%d/%m/%Y %H:%M")
|
||||||
|
|
||||||
|
if periode_pilihan == 'harian':
|
||||||
|
# Jika Harian: Group by JAM (misal "14:00")
|
||||||
|
key = dt_obj.strftime("%H:00")
|
||||||
|
else:
|
||||||
|
# Jika Mingguan/Bulanan: Group by TANGGAL (misal "2025-12-14")
|
||||||
|
key = dt_obj.strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
# Kita hitung JUMLAH TRANSAKSI (+1), bukan total uang
|
||||||
|
trend_data[key] = trend_data.get(key, 0) + 1
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 3. Buat Figure & Plotting
|
||||||
|
try:
|
||||||
|
# Setup layout: 1 Baris, 2 Kolom
|
||||||
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
|
||||||
|
|
||||||
|
judul_periode = periode_pilihan.upper()
|
||||||
|
fig.suptitle(f'LAPORAN TRANSAKSI - {judul_periode}', fontsize=16, fontweight='bold')
|
||||||
|
|
||||||
|
# --- PLOT 1: PIE CHART (Metode Pembayaran) ---
|
||||||
|
if metode_counts:
|
||||||
|
labels = list(metode_counts.keys())
|
||||||
|
sizes = list(metode_counts.values())
|
||||||
|
# Warna-warni pastel agar cantik
|
||||||
|
colors = ['#ff9999','#66b3ff','#99ff99','#ffcc99', '#c2c2f0']
|
||||||
|
ax1.pie(sizes, labels=labels, autopct='%1.0f%%', colors=colors[:len(labels)], startangle=90, shadow=True)
|
||||||
|
ax1.set_title('Proporsi Metode Pembayaran')
|
||||||
|
else:
|
||||||
|
ax1.text(0.5, 0.5, "Tidak ada data", ha='center')
|
||||||
|
|
||||||
|
# --- PLOT 2: BAR CHART (Trend Frekuensi Transaksi) ---
|
||||||
|
if trend_data:
|
||||||
|
keys = sorted(trend_data.keys())
|
||||||
|
counts = [trend_data[k] for k in keys]
|
||||||
|
|
||||||
|
# Label X-Axis (Formatting agar rapi)
|
||||||
|
if periode_pilihan == 'harian':
|
||||||
|
# Label jam tetap apa adanya
|
||||||
|
x_labels = keys
|
||||||
|
x_title = "Jam Transaksi"
|
||||||
|
chart_title = "Jumlah Transaksi per Jam (Busy Hours)"
|
||||||
|
else:
|
||||||
|
# Format tanggal jadi lebih pendek (DD/MM)
|
||||||
|
x_labels = [datetime.strptime(k, "%Y-%m-%d").strftime("%d/%m") for k in keys]
|
||||||
|
x_title = "Tanggal"
|
||||||
|
chart_title = "Total Transaksi per Hari"
|
||||||
|
|
||||||
|
bars = ax2.bar(x_labels, counts, color='#4ECDC4', zorder=3, edgecolor='black', linewidth=0.7)
|
||||||
|
|
||||||
|
ax2.set_title(chart_title)
|
||||||
|
ax2.set_xlabel(x_title)
|
||||||
|
ax2.set_ylabel('Jumlah Transaksi (Struk)')
|
||||||
|
|
||||||
|
# Grid garis putus-putus di belakang
|
||||||
|
ax2.grid(axis='y', linestyle='--', alpha=0.5, zorder=0)
|
||||||
|
|
||||||
|
# Pastikan Sumbu Y hanya menampilkan Angka Bulat (Integer)
|
||||||
|
# Karena tidak mungkin ada 1.5 transaksi
|
||||||
|
ax2.yaxis.set_major_locator(MaxNLocator(integer=True))
|
||||||
|
|
||||||
|
# Tambah label angka di atas setiap batang
|
||||||
|
for bar in bars:
|
||||||
|
height = bar.get_height()
|
||||||
|
ax2.text(bar.get_x() + bar.get_width()/2., height,
|
||||||
|
f'{int(height)}',
|
||||||
|
ha='center', va='bottom', fontsize=10, fontweight='bold')
|
||||||
|
|
||||||
|
# Putar label x jika data banyak agar tidak tabrakan
|
||||||
|
if len(x_labels) > 5:
|
||||||
|
plt.setp(ax2.get_xticklabels(), rotation=45, ha="right")
|
||||||
|
|
||||||
|
else:
|
||||||
|
ax2.text(0.5, 0.5, "Tidak ada data transaksi", ha='center')
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
|
||||||
|
# 4. Dialog Simpan File
|
||||||
|
filename = filedialog.asksaveasfilename(
|
||||||
|
defaultextension=".png",
|
||||||
|
filetypes=[("PNG Image", "*.png"), ("JPEG Image", "*.jpg")],
|
||||||
|
title="Simpan Grafik Statistik"
|
||||||
|
)
|
||||||
|
|
||||||
|
if filename:
|
||||||
|
plt.savefig(filename, dpi=150, bbox_inches='tight')
|
||||||
|
messagebox.showinfo("Sukses", f"Grafik berhasil disimpan:\n{filename}")
|
||||||
|
|
||||||
|
plt.close(fig)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Export Gagal", f"Terjadi kesalahan: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def reload_payment_orders(self):
|
def reload_payment_orders(self):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user