diff --git a/detail_transaksi.csv b/detail_transaksi.csv index 0ca8be3..e8794b3 100644 --- a/detail_transaksi.csv +++ b/detail_transaksi.csv @@ -1,2 +1,4 @@ id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item -1,1,1,1,20000.0,20000.0 +1,2,1,1,20000.0,20000.0 +2,3,1,1,20000.0,20000.0 +3,4,1,1,20000.0,20000.0 diff --git a/favorite.csv b/favorite.csv index 7118a5f..f0f9c08 100644 --- a/favorite.csv +++ b/favorite.csv @@ -1,2 +1,2 @@ user_id,menu_id,order_count,last_ordered -4,1,1,2025-12-13 15:09:50 +4,1,3,2025-12-13 17:22:52 diff --git a/main.py b/main.py index 6dbdf0d..b164fe0 100644 --- a/main.py +++ b/main.py @@ -638,16 +638,129 @@ def apply_discounts_and_promo(cart_items, promo_code=None): +# === FUNGSI FAVORITE === + +def favorite_update(user_id, menu_id): + """Update atau tambah favorite count untuk user tertentu""" + from datetime import datetime + + rows = read_all(FAVORITE_CSV) + found = False + + for r in rows: + if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id): + try: + count = int(r.get("order_count") or 0) + except: + count = 0 + r["order_count"] = str(count + 1) + r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + found = True + break + + if not found: + rows.append({ + "user_id": str(user_id), + "menu_id": str(menu_id), + "order_count": "1", + "last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + + write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows) +def favorite_list(user_id, limit=5): + """Ambil menu favorit user, sorted by order_count descending""" + rows = read_all(FAVORITE_CSV) + out = [] + + for r in rows: + if r.get("user_id") == str(user_id): + try: + mid = int(r.get("menu_id") or 0) + except: + mid = r.get("menu_id") + try: + count = int(r.get("order_count") or 0) + except: + count = 0 + + last_ordered = r.get("last_ordered") + out.append((mid, count, last_ordered)) + + out.sort(key=lambda x: x[1], reverse=True) + + if limit: + out = out[:limit] + + return out +# === FUNGSI PEMBAYARAN === + +def pembayaran_add(transaksi_id, metode_pembayaran, jumlah_bayar, status_pembayaran='sukses', struk=''): + """Simpan data pembayaran baru""" + from datetime import datetime + + rows = read_all(PEMBAYARAN_CSV) + new_id = next_int_id(rows, "id") + tanggal_bayar = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + rows.append({ + "id": new_id, + "transaksi_id": str(transaksi_id), + "metode_pembayaran": metode_pembayaran, + "jumlah_bayar": str(float(jumlah_bayar)), + "status_pembayaran": status_pembayaran, + "tanggal_bayar": tanggal_bayar, + "struk": struk + }) + + write_all(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"], rows) + return new_id +def pembayaran_get_by_transaksi(transaksi_id): + """Ambil data pembayaran berdasarkan transaksi_id""" + rows = read_all(PEMBAYARAN_CSV) + for r in rows: + if r.get("transaksi_id") == str(transaksi_id): + try: + pid = int(r.get("id") or 0) + except: + pid = r.get("id") + try: + jumlah = float(r.get("jumlah_bayar") or 0.0) + except: + jumlah = 0.0 + + return (pid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"), r.get("struk")) + return None +# === FUNGSI MEJA === + +def meja_update_status(nomor_meja, new_status, transaksi_id=""): + """Update status meja (kosong/terisi) dan link ke transaksi""" + rows = read_all(MEJA_CSV) + found = False + + for r in rows: + if r.get("nomor_meja") == str(nomor_meja): + r["status"] = new_status + r["transaksi_id"] = str(transaksi_id) if transaksi_id else "" + found = True + break + + if found: + write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows) + return True + return False +def meja_tutup(nomor_meja): + """Tutup meja (set ke kosong)""" + return meja_update_status(nomor_meja, "kosong", "") # Wilayah dikuasai UI @@ -762,7 +875,7 @@ class App: if self.session['role'] in ['kasir', 'admin']: self.build_payment_tab(self.tab_payment) - + if self.session['role'] == 'admin': self.build_menu_manage_tab(self.tab_menu_manage) self.build_promo_tab(self.tab_promo) @@ -2025,102 +2138,245 @@ class App: # Wilayah dikuasai Favorite -def favorite_update(user_id, menu_id): - """Update atau tambah favorite count untuk user tertentu""" - from datetime import datetime - - rows = read_all(FAVORITE_CSV) - found = False - - for r in rows: - if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id): - # Update count - try: - count = int(r.get("order_count") or 0) - except: - count = 0 - r["order_count"] = str(count + 1) - r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - found = True - break - - if not found: - # Tambah baru - rows.append({ - "user_id": str(user_id), - "menu_id": str(menu_id), - "order_count": "1", - "last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - }) - - write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows) + def favorite_update(user_id, menu_id): + """Update atau tambah favorite count untuk user tertentu""" + from datetime import datetime + + rows = read_all(FAVORITE_CSV) + found = False + + for r in rows: + if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id): + # Update count + try: + count = int(r.get("order_count") or 0) + except: + count = 0 + r["order_count"] = str(count + 1) + r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + found = True + break + + if not found: + # Tambah baru + rows.append({ + "user_id": str(user_id), + "menu_id": str(menu_id), + "order_count": "1", + "last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) + + write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows) -def favorite_list(user_id, limit=5): - """Ambil menu favorit user, sorted by order_count descending""" - rows = read_all(FAVORITE_CSV) - out = [] - - for r in rows: - if r.get("user_id") == str(user_id): - try: - mid = int(r.get("menu_id") or 0) - except: - mid = r.get("menu_id") + def favorite_list(user_id, limit=5): + """Ambil menu favorit user, sorted by order_count descending""" + rows = read_all(FAVORITE_CSV) + out = [] + + for r in rows: + if r.get("user_id") == str(user_id): + try: + mid = int(r.get("menu_id") or 0) + except: + mid = r.get("menu_id") + try: + count = int(r.get("order_count") or 0) + except: + count = 0 + + last_ordered = r.get("last_ordered") + out.append((mid, count, last_ordered)) + + # Sort by count descending + out.sort(key=lambda x: x[1], reverse=True) + + # Limit results + if limit: + out = out[:limit] + + return out + + + def favorite_all(limit=10): + """Ambil menu paling populer dari semua user""" + rows = read_all(FAVORITE_CSV) + menu_counts = {} + + for r in rows: + menu_id = r.get("menu_id") try: count = int(r.get("order_count") or 0) except: count = 0 - last_ordered = r.get("last_ordered") - out.append((mid, count, last_ordered)) - - # Sort by count descending - out.sort(key=lambda x: x[1], reverse=True) - - # Limit results - if limit: - out = out[:limit] - - return out - - -def favorite_all(limit=10): - """Ambil menu paling populer dari semua user""" - rows = read_all(FAVORITE_CSV) - menu_counts = {} - - for r in rows: - menu_id = r.get("menu_id") - try: - count = int(r.get("order_count") or 0) - except: - count = 0 + if menu_id in menu_counts: + menu_counts[menu_id] += count + else: + menu_counts[menu_id] = count - if menu_id in menu_counts: - menu_counts[menu_id] += count - else: - menu_counts[menu_id] = count - - # Convert to list dan sort - out = [] - for menu_id, total_count in menu_counts.items(): - try: - mid = int(menu_id) - except: - mid = menu_id - out.append((mid, total_count)) - - out.sort(key=lambda x: x[1], reverse=True) - - if limit: - out = out[:limit] - - return out + # Convert to list dan sort + out = [] + for menu_id, total_count in menu_counts.items(): + try: + mid = int(menu_id) + except: + mid = menu_id + out.append((mid, total_count)) + + out.sort(key=lambda x: x[1], reverse=True) + + if limit: + out = out[:limit] + + return out + + + + # WILAYAH DIKUASAI PEMBAYARAN & MEJA + + # === FUNGSI PEMBAYARAN === + + + def pembayaran_add(transaksi_id, metode_pembayaran, jumlah_bayar, status_pembayaran='sukses', struk=''): + """Simpan data pembayaran baru""" + from datetime import datetime + + rows = read_all(PEMBAYARAN_CSV) + new_id = next_int_id(rows, "id") + tanggal_bayar = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + rows.append({ + "id": new_id, + "transaksi_id": str(transaksi_id), + "metode_pembayaran": metode_pembayaran, + "jumlah_bayar": str(float(jumlah_bayar)), + "status_pembayaran": status_pembayaran, + "tanggal_bayar": tanggal_bayar, + "struk": struk + }) + + write_all(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"], rows) + return new_id + + + def pembayaran_get_by_transaksi(transaksi_id): + """Ambil data pembayaran berdasarkan transaksi_id""" + rows = read_all(PEMBAYARAN_CSV) + for r in rows: + if r.get("transaksi_id") == str(transaksi_id): + try: + pid = int(r.get("id") or 0) + except: + pid = r.get("id") + try: + jumlah = float(r.get("jumlah_bayar") or 0.0) + except: + jumlah = 0.0 + + return (pid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"), r.get("struk")) + return None + + + def pembayaran_list(status=None, metode=None): + """Ambil semua pembayaran dengan filter opsional""" + rows = read_all(PEMBAYARAN_CSV) + out = [] + + for r in rows: + if status and r.get("status_pembayaran") != status: + continue + if metode and r.get("metode_pembayaran") != metode: + continue + + try: + pid = int(r.get("id") or 0) + except: + pid = r.get("id") + try: + tid = int(r.get("transaksi_id") or 0) + except: + tid = r.get("transaksi_id") + try: + jumlah = float(r.get("jumlah_bayar") or 0.0) + except: + jumlah = 0.0 + + out.append((pid, tid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"))) + + out.sort(key=lambda x: int(x[0]), reverse=True) + return out + + + + # === FUNGSI MEJA === + + + def meja_list(status=None): + """Ambil daftar meja dengan filter status opsional""" + rows = read_all(MEJA_CSV) + out = [] + + for r in rows: + if status and r.get("status") != status: + continue + + try: + nomor = int(r.get("nomor_meja") or 0) + except: + nomor = r.get("nomor_meja") + + transaksi_id = r.get("transaksi_id") or "" + + out.append((nomor, r.get("status"), transaksi_id)) + + out.sort(key=lambda x: int(x[0]) if isinstance(x[0], int) else 0) + return out + + + def meja_get(nomor_meja): + """Ambil data meja berdasarkan nomor""" + rows = read_all(MEJA_CSV) + for r in rows: + if r.get("nomor_meja") == str(nomor_meja): + try: + nomor = int(r.get("nomor_meja") or 0) + except: + nomor = r.get("nomor_meja") + + transaksi_id = r.get("transaksi_id") or "" + return (nomor, r.get("status"), transaksi_id) + return None + + + def meja_update_status(nomor_meja, new_status, transaksi_id=""): + """Update status meja (kosong/terisi) dan link ke transaksi""" + rows = read_all(MEJA_CSV) + found = False + + for r in rows: + if r.get("nomor_meja") == str(nomor_meja): + r["status"] = new_status + r["transaksi_id"] = str(transaksi_id) if transaksi_id else "" + found = True + break + + if found: + write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows) + return True + return False + + + def meja_tutup(nomor_meja): + """Tutup meja (set ke kosong)""" + return meja_update_status(nomor_meja, "kosong", "") + + + def meja_buka(nomor_meja, transaksi_id): + """Buka meja (set ke terisi dengan transaksi_id)""" + return meja_update_status(nomor_meja, "terisi", transaksi_id) - # FITUR PEMBAYARAN - def build_payment_tab(self, parent): """Tab pembayaran untuk kasir & admin""" for w in parent.winfo_children(): @@ -2132,18 +2388,25 @@ def favorite_all(limit=10): ttk.Label(header, text="💰 Pembayaran Transaksi", font=("Arial", 14, "bold")).pack(side='left') ttk.Button(header, text="🔄 Refresh", command=self.reload_payment_orders).pack(side='right', padx=6) - # Split 2 panel: kiri = daftar transaksi, kanan = form pembayaran - left = ttk.LabelFrame(parent, text="📋 Transaksi Siap Dibayar (Status: Selesai)", padding=10) - left.pack(side='left', fill='both', expand=True, padx=10, pady=6) + # Container utama + main_container = ttk.Frame(parent) + main_container.pack(fill='both', expand=True, padx=10, pady=6) - right = ttk.LabelFrame(parent, text="💳 Form Pembayaran", padding=10) - right.pack(side='right', fill='both', expand=True, padx=10, pady=6) + # === PANEL KIRI: Daftar Transaksi === + left = ttk.LabelFrame(main_container, text="📋 Transaksi Siap Dibayar (Status: Selesai)", padding=10) + left.pack(side='left', fill='both', expand=True, padx=(0, 5)) - # === PANEL KIRI: Daftar Transaksi Selesai === + # Treeview transaksi dengan scrollbar + tree_frame = ttk.Frame(left) + tree_frame.pack(fill='both', expand=True) + + tree_scroll = ttk.Scrollbar(tree_frame, orient='vertical') + tree_scroll.pack(side='right', fill='y') - # Treeview transaksi cols = ("ID", "Meja", "Total", "Status", "Tanggal") - self.payment_tree = ttk.Treeview(left, columns=cols, show='headings', height=12) + self.payment_tree = ttk.Treeview(tree_frame, columns=cols, show='headings', height=10, yscrollcommand=tree_scroll.set) + + tree_scroll.config(command=self.payment_tree.yview) self.payment_tree.heading("ID", text="ID") self.payment_tree.heading("Meja", text="Meja") @@ -2155,18 +2418,18 @@ def favorite_all(limit=10): self.payment_tree.column("Meja", width=60) self.payment_tree.column("Total", width=100) self.payment_tree.column("Status", width=80) - self.payment_tree.column("Tanggal", width=140) + self.payment_tree.column("Tanggal", width=120) - self.payment_tree.pack(fill='both', expand=True, pady=6) - - # Bind event + self.payment_tree.pack(side='left', fill='both', expand=True) self.payment_tree.bind("<>", self.on_payment_select) # Detail transaksi detail_frame = ttk.Frame(left) - detail_frame.pack(fill='x', pady=6) + detail_frame.pack(fill='x', pady=(10, 0)) - self.payment_detail_text = tk.Text(detail_frame, height=8, font=("Courier New", 8), wrap='word') + ttk.Label(detail_frame, text="Detail Transaksi:", font=("Arial", 9, "bold")).pack(anchor='w') + + self.payment_detail_text = tk.Text(detail_frame, height=6, font=("Courier New", 8), wrap='word') detail_scroll = ttk.Scrollbar(detail_frame, orient='vertical', command=self.payment_detail_text.yview) self.payment_detail_text.configure(yscrollcommand=detail_scroll.set) @@ -2174,10 +2437,50 @@ def favorite_all(limit=10): detail_scroll.pack(side='right', fill='y') # === PANEL KANAN: Form Pembayaran === + right_outer = ttk.LabelFrame(main_container, text="💳 Form Pembayaran", padding=5) + right_outer.pack(side='right', fill='both', expand=True, padx=(5, 0)) - # Info transaksi terpilih + # Frame wrapper untuk canvas + wrapper = ttk.Frame(right_outer) + wrapper.pack(fill='both', expand=True) + + # Scrollbar + scrollbar = ttk.Scrollbar(wrapper, orient='vertical') + scrollbar.pack(side='right', fill='y') + + # Canvas dengan HEIGHT FIXED + canvas = tk.Canvas(wrapper, yscrollcommand=scrollbar.set, highlightthickness=0, height=450) + canvas.pack(side='left', fill='both', expand=True) + + scrollbar.config(command=canvas.yview) + + # Frame konten + right = ttk.Frame(canvas) + canvas_window = canvas.create_window((0, 0), window=right, anchor='nw') + + # Update scroll region + def configure_scroll(event): + canvas.configure(scrollregion=canvas.bbox('all')) + + right.bind('', configure_scroll) + + # Update width + def configure_canvas(event): + canvas.itemconfig(canvas_window, width=event.width) + + canvas.bind('', configure_canvas) + + # Mouse wheel + def on_mousewheel(event): + canvas.yview_scroll(-1 * int(event.delta / 120), "units") + + canvas.bind_all('', on_mousewheel) + + # === ISI FORM === + + # Info transaksi info_frame = ttk.Frame(right) - info_frame.pack(fill='x', pady=10) + info_frame.pack(fill='x', pady=15, padx=15) self.selected_transaksi_label = ttk.Label(info_frame, text="Belum ada transaksi dipilih", font=("Arial", 10, "bold"), foreground='red') self.selected_transaksi_label.pack() @@ -2185,443 +2488,451 @@ def favorite_all(limit=10): self.selected_total_label = ttk.Label(info_frame, text="Total: Rp 0", font=("Arial", 12, "bold"), foreground='green') self.selected_total_label.pack(pady=4) - ttk.Separator(right, orient='horizontal').pack(fill='x', pady=10) + ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15) - # Pilih metode pembayaran + # Metode pembayaran method_frame = ttk.Frame(right) - method_frame.pack(fill='x', pady=10) + method_frame.pack(fill='x', pady=10, padx=15) - ttk.Label(method_frame, text="💳 Pilih Metode Pembayaran:", font=("Arial", 10, "bold")).pack(anchor='w', pady=4) + ttk.Label(method_frame, text="💳 Pilih Metode Pembayaran:", font=("Arial", 10, "bold")).pack(anchor='w', pady=6) self.payment_method_var = tk.StringVar(value='cash') - ttk.Radiobutton(method_frame, text="💵 Cash", variable=self.payment_method_var, value='cash', command=self.on_payment_method_change).pack(anchor='w', pady=2) - ttk.Radiobutton(method_frame, text="📱 QRIS", variable=self.payment_method_var, value='qris', command=self.on_payment_method_change).pack(anchor='w', pady=2) - ttk.Radiobutton(method_frame, text="💳 E-Wallet (GoPay/OVO/Dana)", variable=self.payment_method_var, value='ewallet', command=self.on_payment_method_change).pack(anchor='w', pady=2) + ttk.Radiobutton(method_frame, text="💵 Cash", variable=self.payment_method_var, value='cash', command=self.on_payment_method_change).pack(anchor='w', pady=3) + ttk.Radiobutton(method_frame, text="📱 QRIS", variable=self.payment_method_var, value='qris', command=self.on_payment_method_change).pack(anchor='w', pady=3) + ttk.Radiobutton(method_frame, text="💳 E-Wallet (GoPay/OVO/Dana)", variable=self.payment_method_var, value='ewallet', command=self.on_payment_method_change).pack(anchor='w', pady=3) - ttk.Separator(right, orient='horizontal').pack(fill='x', pady=10) + ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15) - # Frame dinamis untuk input (berganti sesuai metode) + # Frame input dinamis self.payment_input_frame = ttk.Frame(right) - self.payment_input_frame.pack(fill='both', expand=True, pady=10) + self.payment_input_frame.pack(fill='x', pady=10, padx=15) - # Init cash input (default) self.build_cash_input() - # Tombol proses pembayaran - ttk.Button(right, text="✅ PROSES PEMBAYARAN", command=self.process_payment, style="Accent.TButton").pack(pady=15, ipadx=20, ipady=10) + ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15) + + # Tombol + btn_frame = ttk.Frame(right) + btn_frame.pack(fill='x', pady=20, padx=15) + + ttk.Button(btn_frame, text="✅ PROSES PEMBAYARAN", command=self.process_payment, style="Accent.TButton").pack(ipadx=30, ipady=10) + + # PENTING: Tambah banyak space di bawah biar scrollbar muncul + for i in range(20): + ttk.Label(right, text="").pack() # Load data self.reload_payment_orders() - + def reload_payment_orders(self): - """Load transaksi dengan status 'selesai' yang belum dibayar""" - # Clear tree - for r in self.payment_tree.get_children(): - self.payment_tree.delete(r) - - # Get transaksi selesai - orders = transaksi_list(status='selesai') - - # Filter yang belum ada pembayaran - for order in orders: - tid, uid, meja, total, status, promo_code, tanggal = order + """Load transaksi dengan status 'selesai' yang belum dibayar""" + # Clear tree + for r in self.payment_tree.get_children(): + self.payment_tree.delete(r) - # Cek apakah sudah ada pembayaran - payment_data = pembayaran_get_by_transaksi(tid) - if payment_data: - continue # Skip jika sudah dibayar + # Get transaksi selesai + orders = transaksi_list(status='selesai') - self.payment_tree.insert("", tk.END, values=( - tid, meja, f"Rp {total:,.0f}", status, tanggal - )) - + # Filter yang belum ada pembayaran + for order in orders: + tid, uid, meja, total, status, promo_code, tanggal = order + + # Cek apakah sudah ada pembayaran + payment_data = pembayaran_get_by_transaksi(tid) + if payment_data: + continue # Skip jika sudah dibayar + + self.payment_tree.insert("", tk.END, values=( + tid, meja, f"Rp {total:,.0f}", status, tanggal + )) + def on_payment_select(self, event): - """Tampilkan detail transaksi saat dipilih""" - sel = self.payment_tree.selection() - if not sel: - return - - item = self.payment_tree.item(sel)['values'] - transaksi_id = item[0] - - # Get detail transaksi - transaksi_data = transaksi_get(transaksi_id) - if not transaksi_data: - return - - tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data - detail_items = detail_transaksi_list(transaksi_id) - - # Update label - self.selected_transaksi_label.config(text=f"Transaksi #{tid} - Meja {meja}", foreground='blue') - self.selected_total_label.config(text=f"Total: Rp {total:,.0f}") - - # Format detail - detail_text = f"═══════════════════════════════════════════════\n" - detail_text += f"TRANSAKSI #{tid}\n" - detail_text += f"═══════════════════════════════════════════════\n\n" - detail_text += f"Meja : {meja}\n" - detail_text += f"Tanggal : {tanggal}\n" - detail_text += f"Status : {status.upper()}\n" - detail_text += f"Promo : {promo_code if promo_code else '-'}\n\n" - - detail_text += f"───────────────────────────────────────────────\n" - detail_text += f"ITEM PESANAN:\n" - detail_text += f"───────────────────────────────────────────────\n" - - for detail in detail_items: - did, mid, qty, harga, subtotal_item = detail - menu_data = menu_get(mid) - if menu_data: - _, nama, kategori, _, _, _, _, _ = menu_data - detail_text += f"• {nama}\n" - detail_text += f" {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n\n" - - detail_text += f"───────────────────────────────────────────────\n" - detail_text += f"Subtotal : Rp {subtotal:,.0f}\n" - detail_text += f"Diskon : Rp {item_disc + promo_disc:,.0f}\n" - detail_text += f"───────────────────────────────────────────────\n" - detail_text += f"TOTAL BAYAR : Rp {total:,.0f}\n" - detail_text += f"═══════════════════════════════════════════════\n" - - self.payment_detail_text.delete('1.0', tk.END) - self.payment_detail_text.insert('1.0', detail_text) - - def on_payment_method_change(self): - """Ganti input form sesuai metode pembayaran""" - method = self.payment_method_var.get() - - # Clear frame - for w in self.payment_input_frame.winfo_children(): - w.destroy() - - if method == 'cash': - self.build_cash_input() - elif method == 'qris': - self.build_qris_input() - elif method == 'ewallet': - self.build_ewallet_input() - - def build_cash_input(self): - """Form input untuk pembayaran cash""" - ttk.Label(self.payment_input_frame, text="💵 Pembayaran Cash", font=("Arial", 10, "bold")).pack(pady=6) - - input_frame = ttk.Frame(self.payment_input_frame) - input_frame.pack(fill='x', pady=6) - - ttk.Label(input_frame, text="Jumlah Bayar:").grid(row=0, column=0, sticky='w', pady=4) - self.cash_amount_var = tk.StringVar() - ttk.Entry(input_frame, textvariable=self.cash_amount_var, width=20).grid(row=0, column=1, pady=4, sticky='ew') - - input_frame.columnconfigure(1, weight=1) - - # Label kembalian - self.cash_change_label = ttk.Label(self.payment_input_frame, text="Kembalian: Rp 0", font=("Arial", 10), foreground='green') - self.cash_change_label.pack(pady=6) - - # Bind event untuk hitung kembalian real-time - self.cash_amount_var.trace('w', self.calculate_cash_change) - - def calculate_cash_change(self, *args): - """Hitung kembalian cash secara real-time""" - sel = self.payment_tree.selection() - if not sel: - return - - item = self.payment_tree.item(sel)['values'] - transaksi_id = item[0] - - transaksi_data = transaksi_get(transaksi_id) - if not transaksi_data: - return - - total = transaksi_data[3] - - try: - cash_input = float(self.cash_amount_var.get() or 0) - change = cash_input - total + """Tampilkan detail transaksi saat dipilih""" + sel = self.payment_tree.selection() + if not sel: + return - if change < 0: - self.cash_change_label.config(text=f"Kembalian: Kurang Rp {abs(change):,.0f}", foreground='red') - else: - self.cash_change_label.config(text=f"Kembalian: Rp {change:,.0f}", foreground='green') - except: - self.cash_change_label.config(text="Kembalian: Rp 0", foreground='gray') - - def build_qris_input(self): - """Form input untuk pembayaran QRIS (simulasi QR Code)""" - ttk.Label(self.payment_input_frame, text="📱 Pembayaran QRIS", font=("Arial", 10, "bold")).pack(pady=6) + item = self.payment_tree.item(sel)['values'] + transaksi_id = item[0] + + # Get detail transaksi + transaksi_data = transaksi_get(transaksi_id) + if not transaksi_data: + return + + tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data + detail_items = detail_transaksi_list(transaksi_id) + + # Update label + self.selected_transaksi_label.config(text=f"Transaksi #{tid} - Meja {meja}", foreground='blue') + self.selected_total_label.config(text=f"Total: Rp {total:,.0f}") + + # Format detail + detail_text = f"═══════════════════════════════════════════════\n" + detail_text += f"TRANSAKSI #{tid}\n" + detail_text += f"═══════════════════════════════════════════════\n\n" + detail_text += f"Meja : {meja}\n" + detail_text += f"Tanggal : {tanggal}\n" + detail_text += f"Status : {status.upper()}\n" + detail_text += f"Promo : {promo_code if promo_code else '-'}\n\n" + + detail_text += f"───────────────────────────────────────────────\n" + detail_text += f"ITEM PESANAN:\n" + detail_text += f"───────────────────────────────────────────────\n" + + for detail in detail_items: + did, mid, qty, harga, subtotal_item = detail + menu_data = menu_get(mid) + if menu_data: + _, nama, kategori, _, _, _, _, _ = menu_data + detail_text += f"• {nama}\n" + detail_text += f" {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n\n" + + detail_text += f"───────────────────────────────────────────────\n" + detail_text += f"Subtotal : Rp {subtotal:,.0f}\n" + detail_text += f"Diskon : Rp {item_disc + promo_disc:,.0f}\n" + detail_text += f"───────────────────────────────────────────────\n" + detail_text += f"TOTAL BAYAR : Rp {total:,.0f}\n" + detail_text += f"═══════════════════════════════════════════════\n" + + self.payment_detail_text.delete('1.0', tk.END) + self.payment_detail_text.insert('1.0', detail_text) - ttk.Label(self.payment_input_frame, text="Scan QR Code di bawah untuk bayar:", font=("Arial", 9)).pack(pady=4) + def on_payment_method_change(self): + """Ganti input form sesuai metode pembayaran""" + method = self.payment_method_var.get() + + # Clear frame + for w in self.payment_input_frame.winfo_children(): + w.destroy() + + if method == 'cash': + self.build_cash_input() + elif method == 'qris': + self.build_qris_input() + elif method == 'ewallet': + self.build_ewallet_input() - # Generate QR Code - sel = self.payment_tree.selection() - if sel: + def build_cash_input(self): + """Form input untuk pembayaran cash""" + ttk.Label(self.payment_input_frame, text="💵 Pembayaran Cash", font=("Arial", 10, "bold")).pack(pady=6) + + input_frame = ttk.Frame(self.payment_input_frame) + input_frame.pack(fill='x', pady=6) + + ttk.Label(input_frame, text="Jumlah Bayar:").grid(row=0, column=0, sticky='w', pady=4) + self.cash_amount_var = tk.StringVar() + ttk.Entry(input_frame, textvariable=self.cash_amount_var, width=20).grid(row=0, column=1, pady=4, sticky='ew') + + input_frame.columnconfigure(1, weight=1) + + # Label kembalian + self.cash_change_label = ttk.Label(self.payment_input_frame, text="Kembalian: Rp 0", font=("Arial", 10), foreground='green') + self.cash_change_label.pack(pady=6) + + # Bind event untuk hitung kembalian real-time + self.cash_amount_var.trace('w', self.calculate_cash_change) + + def calculate_cash_change(self, *args): + """Hitung kembalian cash secara real-time""" + sel = self.payment_tree.selection() + if not sel: + return + item = self.payment_tree.item(sel)['values'] transaksi_id = item[0] transaksi_data = transaksi_get(transaksi_id) - if transaksi_data: - total = transaksi_data[3] + if not transaksi_data: + return + + total = transaksi_data[3] + + try: + cash_input = float(self.cash_amount_var.get() or 0) + change = cash_input - total - # QR Code data (simulasi) - qr_data = f"QRIS-CAFE-TOTORO-TRX{transaksi_id}-TOTAL{int(total)}" - - try: - import qrcode - qr = qrcode.QRCode(version=1, box_size=5, border=2) - qr.add_data(qr_data) - qr.make(fit=True) - - qr_img = qr.make_image(fill_color="black", back_color="white") - qr_img = qr_img.resize((200, 200), Image.Resampling.LANCZOS) - - qr_photo = ImageTk.PhotoImage(qr_img) - self.img_cache['qris'] = qr_photo - - qr_label = ttk.Label(self.payment_input_frame, image=qr_photo) - qr_label.pack(pady=10) - - except ImportError: - ttk.Label(self.payment_input_frame, text="[QR Code - Install qrcode library]", background='#e0e0e0', font=("Arial", 9)).pack(pady=10) - - ttk.Label(self.payment_input_frame, text="⚠️ Ini adalah SIMULASI QRIS", font=("Arial", 8), foreground='orange').pack(pady=6) - ttk.Label(self.payment_input_frame, text="Klik 'PROSES PEMBAYARAN' untuk konfirmasi", font=("Arial", 8)).pack() + if change < 0: + self.cash_change_label.config(text=f"Kembalian: Kurang Rp {abs(change):,.0f}", foreground='red') + else: + self.cash_change_label.config(text=f"Kembalian: Rp {change:,.0f}", foreground='green') + except: + self.cash_change_label.config(text="Kembalian: Rp 0", foreground='gray') + + def build_qris_input(self): + """Form input untuk pembayaran QRIS (simulasi QR Code)""" + ttk.Label(self.payment_input_frame, text="📱 Pembayaran QRIS", font=("Arial", 10, "bold")).pack(pady=6) + + ttk.Label(self.payment_input_frame, text="Scan QR Code di bawah untuk bayar:", font=("Arial", 9)).pack(pady=4) + + # Generate QR Code + sel = self.payment_tree.selection() + if sel: + item = self.payment_tree.item(sel)['values'] + transaksi_id = item[0] + + transaksi_data = transaksi_get(transaksi_id) + if transaksi_data: + total = transaksi_data[3] + + # QR Code data (simulasi) + qr_data = f"QRIS-CAFE-TOTORO-TRX{transaksi_id}-TOTAL{int(total)}" + + try: + import qrcode + qr = qrcode.QRCode(version=1, box_size=5, border=2) + qr.add_data(qr_data) + qr.make(fit=True) + + qr_img = qr.make_image(fill_color="black", back_color="white") + qr_img = qr_img.resize((200, 200), Image.Resampling.LANCZOS) + + qr_photo = ImageTk.PhotoImage(qr_img) + self.img_cache['qris'] = qr_photo + + qr_label = ttk.Label(self.payment_input_frame, image=qr_photo) + qr_label.pack(pady=10) + + except ImportError: + ttk.Label(self.payment_input_frame, text="[QR Code - Install qrcode library]", background='#e0e0e0', font=("Arial", 9)).pack(pady=10) + + ttk.Label(self.payment_input_frame, text="⚠️ Ini adalah SIMULASI QRIS", font=("Arial", 8), foreground='orange').pack(pady=6) + ttk.Label(self.payment_input_frame, text="Klik 'PROSES PEMBAYARAN' untuk konfirmasi", font=("Arial", 8)).pack() + def build_ewallet_input(self): - """Form input untuk pembayaran E-Wallet (simulasi)""" - ttk.Label(self.payment_input_frame, text="💳 Pembayaran E-Wallet", font=("Arial", 10, "bold")).pack(pady=6) + """Form input untuk pembayaran E-Wallet (simulasi)""" + ttk.Label(self.payment_input_frame, text="💳 Pembayaran E-Wallet", font=("Arial", 10, "bold")).pack(pady=6) + + ttk.Label(self.payment_input_frame, text="Pilih E-Wallet:", font=("Arial", 9)).pack(pady=4) + + self.ewallet_type_var = tk.StringVar(value='gopay') + + ewallet_frame = ttk.Frame(self.payment_input_frame) + ewallet_frame.pack(pady=6) + + ttk.Radiobutton(ewallet_frame, text="GoPay", variable=self.ewallet_type_var, value='gopay').pack(anchor='w', pady=2) + ttk.Radiobutton(ewallet_frame, text="OVO", variable=self.ewallet_type_var, value='ovo').pack(anchor='w', pady=2) + ttk.Radiobutton(ewallet_frame, text="Dana", variable=self.ewallet_type_var, value='dana').pack(anchor='w', pady=2) + ttk.Radiobutton(ewallet_frame, text="ShopeePay", variable=self.ewallet_type_var, value='shopeepay').pack(anchor='w', pady=2) + + ttk.Label(self.payment_input_frame, text="⚠️ Ini adalah SIMULASI E-Wallet", font=("Arial", 8), foreground='orange').pack(pady=6) + ttk.Label(self.payment_input_frame, text="Klik 'PROSES PEMBAYARAN' untuk konfirmasi", font=("Arial", 8)).pack() - ttk.Label(self.payment_input_frame, text="Pilih E-Wallet:", font=("Arial", 9)).pack(pady=4) - - self.ewallet_type_var = tk.StringVar(value='gopay') - - ewallet_frame = ttk.Frame(self.payment_input_frame) - ewallet_frame.pack(pady=6) - - ttk.Radiobutton(ewallet_frame, text="GoPay", variable=self.ewallet_type_var, value='gopay').pack(anchor='w', pady=2) - ttk.Radiobutton(ewallet_frame, text="OVO", variable=self.ewallet_type_var, value='ovo').pack(anchor='w', pady=2) - ttk.Radiobutton(ewallet_frame, text="Dana", variable=self.ewallet_type_var, value='dana').pack(anchor='w', pady=2) - ttk.Radiobutton(ewallet_frame, text="ShopeePay", variable=self.ewallet_type_var, value='shopeepay').pack(anchor='w', pady=2) - - ttk.Label(self.payment_input_frame, text="⚠️ Ini adalah SIMULASI E-Wallet", font=("Arial", 8), foreground='orange').pack(pady=6) - ttk.Label(self.payment_input_frame, text="Klik 'PROSES PEMBAYARAN' untuk konfirmasi", font=("Arial", 8)).pack() - def process_payment(self): - """Proses pembayaran sesuai metode yang dipilih""" - # Cek apakah ada transaksi terpilih - sel = self.payment_tree.selection() - if not sel: - messagebox.showwarning("Pilih Transaksi", "Pilih transaksi yang akan dibayar") - return - - item = self.payment_tree.item(sel)['values'] - transaksi_id = item[0] - - transaksi_data = transaksi_get(transaksi_id) - if not transaksi_data: - messagebox.showerror("Error", "Data transaksi tidak ditemukan") - return - - tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data - - # Get metode pembayaran - method = self.payment_method_var.get() - - # Validasi & proses sesuai metode - if method == 'cash': - success, message = self.process_cash_payment(tid, total) - elif method == 'qris': - success, message = self.process_qris_payment(tid, total) - elif method == 'ewallet': - success, message = self.process_ewallet_payment(tid, total) - else: - messagebox.showerror("Error", "Metode pembayaran tidak valid") - return - - if success: - # Generate struk - struk = self.generate_struk(tid) + """Proses pembayaran sesuai metode yang dipilih""" + # Cek apakah ada transaksi terpilih + sel = self.payment_tree.selection() + if not sel: + messagebox.showwarning("Pilih Transaksi", "Pilih transaksi yang akan dibayar") + return - # Update status transaksi jadi 'dibayar' - transaksi_update_status(tid, 'dibayar') + item = self.payment_tree.item(sel)['values'] + transaksi_id = item[0] - # Tutup meja - meja_tutup(meja) + transaksi_data = transaksi_get(transaksi_id) + if not transaksi_data: + messagebox.showerror("Error", "Data transaksi tidak ditemukan") + return - # Tampilkan struk - self.show_struk(struk) + tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data - # Reload data - self.reload_payment_orders() + # Get metode pembayaran + method = self.payment_method_var.get() - # Reset selection - self.selected_transaksi_label.config(text="Belum ada transaksi dipilih", foreground='red') - self.selected_total_label.config(text="Total: Rp 0") - self.payment_detail_text.delete('1.0', tk.END) - else: - messagebox.showerror("Pembayaran Gagal", message) - + # Validasi & proses sesuai metode + if method == 'cash': + success, message = self.process_cash_payment(tid, total) + elif method == 'qris': + success, message = self.process_qris_payment(tid, total) + elif method == 'ewallet': + success, message = self.process_ewallet_payment(tid, total) + else: + messagebox.showerror("Error", "Metode pembayaran tidak valid") + return + + if success: + # Generate struk + struk = self.generate_struk(tid) + + # Update status transaksi jadi 'dibayar' + transaksi_update_status(tid, 'dibayar') + + # Tutup meja + meja_tutup(meja) + + # Tampilkan struk + self.show_struk(struk) + + # Reload data + self.reload_payment_orders() + + # Reset selection + self.selected_transaksi_label.config(text="Belum ada transaksi dipilih", foreground='red') + self.selected_total_label.config(text="Total: Rp 0") + self.payment_detail_text.delete('1.0', tk.END) + else: + messagebox.showerror("Pembayaran Gagal", message) + def process_cash_payment(self, transaksi_id, total): - """Proses pembayaran cash""" - try: - cash_input = float(self.cash_amount_var.get() or 0) - except: - return False, "Jumlah bayar tidak valid" + """Proses pembayaran cash""" + try: + cash_input = float(self.cash_amount_var.get() or 0) + except: + return False, "Jumlah bayar tidak valid" + + if cash_input < total: + return False, f"Uang kurang! Total: Rp {total:,.0f}, Bayar: Rp {cash_input:,.0f}" + + # Simpan pembayaran + pembayaran_add(transaksi_id, 'cash', cash_input, 'sukses', '') + + return True, "Pembayaran cash berhasil" - if cash_input < total: - return False, f"Uang kurang! Total: Rp {total:,.0f}, Bayar: Rp {cash_input:,.0f}" - - # Simpan pembayaran - pembayaran_add(transaksi_id, 'cash', cash_input, 'sukses', '') - - return True, "Pembayaran cash berhasil" - def process_qris_payment(self, transaksi_id, total): - """Proses pembayaran QRIS (simulasi)""" - # Konfirmasi - confirm = messagebox.askyesno("Konfirmasi QRIS", - f"Pembayaran QRIS sebesar Rp {total:,.0f}\n\n" - f"⚠️ SIMULASI: Anggap customer sudah scan QR Code\n\n" - f"Lanjutkan pembayaran?") - - if not confirm: - return False, "Pembayaran dibatalkan" - - # Simpan pembayaran - pembayaran_add(transaksi_id, 'qris', total, 'sukses', '') - - return True, "Pembayaran QRIS berhasil" - + """Proses pembayaran QRIS (simulasi)""" + # Konfirmasi + confirm = messagebox.askyesno("Konfirmasi QRIS", + f"Pembayaran QRIS sebesar Rp {total:,.0f}\n\n" + f"⚠️ SIMULASI: Anggap customer sudah scan QR Code\n\n" + f"Lanjutkan pembayaran?") + + if not confirm: + return False, "Pembayaran dibatalkan" + + # Simpan pembayaran + pembayaran_add(transaksi_id, 'qris', total, 'sukses', '') + + return True, "Pembayaran QRIS berhasil" + def process_ewallet_payment(self, transaksi_id, total): - """Proses pembayaran E-Wallet (simulasi)""" - ewallet_type = self.ewallet_type_var.get() - ewallet_name = ewallet_type.upper() + """Proses pembayaran E-Wallet (simulasi)""" + ewallet_type = self.ewallet_type_var.get() + ewallet_name = ewallet_type.upper() + + # Konfirmasi + confirm = messagebox.askyesno("Konfirmasi E-Wallet", + f"Pembayaran {ewallet_name} sebesar Rp {total:,.0f}\n\n" + f"⚠️ SIMULASI: Anggap customer sudah konfirmasi di app\n\n" + f"Lanjutkan pembayaran?") + + if not confirm: + return False, "Pembayaran dibatalkan" + + # Simpan pembayaran + pembayaran_add(transaksi_id, f'ewallet-{ewallet_type}', total, 'sukses', '') + + return True, f"Pembayaran {ewallet_name} berhasil" - # Konfirmasi - confirm = messagebox.askyesno("Konfirmasi E-Wallet", - f"Pembayaran {ewallet_name} sebesar Rp {total:,.0f}\n\n" - f"⚠️ SIMULASI: Anggap customer sudah konfirmasi di app\n\n" - f"Lanjutkan pembayaran?") - - if not confirm: - return False, "Pembayaran dibatalkan" - - # Simpan pembayaran - pembayaran_add(transaksi_id, f'ewallet-{ewallet_type}', total, 'sukses', '') - - return True, f"Pembayaran {ewallet_name} berhasil" - def generate_struk(self, transaksi_id): - """Generate struk transaksi""" - transaksi_data = transaksi_get(transaksi_id) - if not transaksi_data: - return "" + """Generate struk transaksi""" + transaksi_data = transaksi_get(transaksi_id) + if not transaksi_data: + return "" + + tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data + detail_items = detail_transaksi_list(transaksi_id) + payment_data = pembayaran_get_by_transaksi(transaksi_id) + + struk = "═════════════════════════════════════════\n" + struk += " CAFE TOTORO MANIA\n" + struk += " Jl. Raya Kampus No. 123, Surabaya\n" + struk += " Telp: 031-123456\n" + struk += "═════════════════════════════════════════\n\n" + struk += f"No. Transaksi : {tid}\n" + struk += f"Tanggal : {tanggal}\n" + struk += f"Meja : {meja}\n" + struk += f"Kasir : {self.session['username']}\n" + + if payment_data: + metode = payment_data[1] + struk += f"Pembayaran : {metode.upper()}\n" + + struk += "─────────────────────────────────────────\n" + struk += "ITEM PESANAN:\n" + struk += "─────────────────────────────────────────\n" + + for detail in detail_items: + did, mid, qty, harga, subtotal_item = detail + menu_data = menu_get(mid) + if menu_data: + _, nama, kategori, _, _, _, _, _ = menu_data + struk += f"{nama}\n" + struk += f" {qty} x Rp {harga:,.0f}".ljust(30) + struk += f"Rp {subtotal_item:,.0f}\n" + + struk += "─────────────────────────────────────────\n" + struk += f"Subtotal : Rp {subtotal:,.0f}\n" + + if item_disc > 0: + struk += f"Diskon Item : Rp {item_disc:,.0f}\n" + + if promo_disc > 0: + struk += f"Diskon Promo : Rp {promo_disc:,.0f}\n" + + struk += "─────────────────────────────────────────\n" + struk += f"TOTAL BAYAR : Rp {total:,.0f}\n" + + # Jika cash, tampilkan bayar & kembalian + if payment_data and payment_data[1] == 'cash': + jumlah_bayar = payment_data[2] + kembalian = jumlah_bayar - total + struk += f"Bayar : Rp {jumlah_bayar:,.0f}\n" + struk += f"Kembalian : Rp {kembalian:,.0f}\n" + + struk += "═════════════════════════════════════════\n" + struk += " TERIMA KASIH ATAS KUNJUNGAN ANDA\n" + struk += " SAMPAI JUMPA LAGI!\n" + struk += "═════════════════════════════════════════\n" + + return struk - tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data - detail_items = detail_transaksi_list(transaksi_id) - payment_data = pembayaran_get_by_transaksi(transaksi_id) - - struk = "═════════════════════════════════════════\n" - struk += " CAFE TOTORO MANIA\n" - struk += " Jl. Raya Kampus No. 123, Surabaya\n" - struk += " Telp: 031-123456\n" - struk += "═════════════════════════════════════════\n\n" - struk += f"No. Transaksi : {tid}\n" - struk += f"Tanggal : {tanggal}\n" - struk += f"Meja : {meja}\n" - struk += f"Kasir : {self.session['username']}\n" - - if payment_data: - metode = payment_data[1] - struk += f"Pembayaran : {metode.upper()}\n" - - struk += "─────────────────────────────────────────\n" - struk += "ITEM PESANAN:\n" - struk += "─────────────────────────────────────────\n" - - for detail in detail_items: - did, mid, qty, harga, subtotal_item = detail - menu_data = menu_get(mid) - if menu_data: - _, nama, kategori, _, _, _, _, _ = menu_data - struk += f"{nama}\n" - struk += f" {qty} x Rp {harga:,.0f}".ljust(30) - struk += f"Rp {subtotal_item:,.0f}\n" - - struk += "─────────────────────────────────────────\n" - struk += f"Subtotal : Rp {subtotal:,.0f}\n" - - if item_disc > 0: - struk += f"Diskon Item : Rp {item_disc:,.0f}\n" - - if promo_disc > 0: - struk += f"Diskon Promo : Rp {promo_disc:,.0f}\n" - - struk += "─────────────────────────────────────────\n" - struk += f"TOTAL BAYAR : Rp {total:,.0f}\n" - - # Jika cash, tampilkan bayar & kembalian - if payment_data and payment_data[1] == 'cash': - jumlah_bayar = payment_data[2] - kembalian = jumlah_bayar - total - struk += f"Bayar : Rp {jumlah_bayar:,.0f}\n" - struk += f"Kembalian : Rp {kembalian:,.0f}\n" - - struk += "═════════════════════════════════════════\n" - struk += " TERIMA KASIH ATAS KUNJUNGAN ANDA\n" - struk += " SAMPAI JUMPA LAGI!\n" - struk += "═════════════════════════════════════════\n" - - return struk - def show_struk(self, struk): - """Tampilkan struk dalam popup window""" - w = tk.Toplevel(self.root) - w.title("✅ Pembayaran Berhasil - Struk Transaksi") - w.geometry("500x600") - w.transient(self.root) - w.grab_set() - - # Frame untuk struk - frm = ttk.Frame(w, padding=15) - frm.pack(fill='both', expand=True) - - ttk.Label(frm, text="✅ PEMBAYARAN BERHASIL!", font=("Arial", 14, "bold"), foreground='green').pack(pady=10) - - # Text widget untuk struk - struk_text = tk.Text(frm, width=50, height=28, font=("Courier New", 9), wrap='word') - struk_scroll = ttk.Scrollbar(frm, orient='vertical', command=struk_text.yview) - struk_text.configure(yscrollcommand=struk_scroll.set) - - struk_text.insert('1.0', struk) - struk_text.config(state='disabled') # Read-only - - struk_text.pack(side='left', fill='both', expand=True) - struk_scroll.pack(side='right', fill='y') - - # Tombol - btn_frame = ttk.Frame(w) - btn_frame.pack(pady=10) - - ttk.Button(btn_frame, text="🖨️ Print (Simulasi)", command=lambda: self.print_struk_simulation(struk)).pack(side='left', padx=6) - ttk.Button(btn_frame, text="✅ Tutup", command=w.destroy).pack(side='left', padx=6) - -def print_struk_simulation(self, struk): - """Simulasi print struk (save to file)""" - from datetime import datetime + """Tampilkan struk dalam popup window""" + w = tk.Toplevel(self.root) + w.title("✅ Pembayaran Berhasil - Struk Transaksi") + w.geometry("500x600") + w.transient(self.root) + w.grab_set() + + # Frame untuk struk + frm = ttk.Frame(w, padding=15) + frm.pack(fill='both', expand=True) + + ttk.Label(frm, text="✅ PEMBAYARAN BERHASIL!", font=("Arial", 14, "bold"), foreground='green').pack(pady=10) + + # Text widget untuk struk + struk_text = tk.Text(frm, width=50, height=28, font=("Courier New", 9), wrap='word') + struk_scroll = ttk.Scrollbar(frm, orient='vertical', command=struk_text.yview) + struk_text.configure(yscrollcommand=struk_scroll.set) + + struk_text.insert('1.0', struk) + struk_text.config(state='disabled') # Read-only + + struk_text.pack(side='left', fill='both', expand=True) + struk_scroll.pack(side='right', fill='y') + + # Tombol + btn_frame = ttk.Frame(w) + btn_frame.pack(pady=10) + + ttk.Button(btn_frame, text="🖨️ Print (Simulasi)", command=lambda: self.print_struk_simulation(struk)).pack(side='left', padx=6) + ttk.Button(btn_frame, text="✅ Tutup", command=w.destroy).pack(side='left', padx=6) - filename = f"struk_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt" - - try: - with open(filename, 'w', encoding='utf-8') as f: - f.write(struk) - - messagebox.showinfo("Print Simulasi", f"Struk berhasil disimpan ke file:\n{filename}") - except Exception as e: - messagebox.showerror("Error", f"Gagal menyimpan struk: {e}") - + def print_struk_simulation(self, struk): + """Simulasi print struk (save to file)""" + from datetime import datetime + + filename = f"struk_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt" + + try: + with open(filename, 'w', encoding='utf-8') as f: + f.write(struk) + + messagebox.showinfo("Print Simulasi", f"Struk berhasil disimpan ke file:\n{filename}") + except Exception as e: + messagebox.showerror("Error", f"Gagal menyimpan struk: {e}") diff --git a/menu.csv b/menu.csv index 1e52d66..6584bfa 100644 --- a/menu.csv +++ b/menu.csv @@ -1,5 +1,5 @@ id,nama,kategori,harga,stok,foto,tersedia,item_discount_pct -1,Americano,Minuman,20000,4,,1,0 +1,Americano,Minuman,20000,1,,1,0 2,Latte,Minuman,25000,1,,1,10 3,Banana Cake,Dessert,30000,0,,0,0 4,Nasi Goreng,Makanan,35000,0,,0,0 diff --git a/pembayaran.csv b/pembayaran.csv index f6e0682..252c4fb 100644 --- a/pembayaran.csv +++ b/pembayaran.csv @@ -1 +1,2 @@ -id,transaksi_id,metode_pembayaran,jumlah_bayar,status_pembayaran,tanggal_bayar,struk \ No newline at end of file +id,transaksi_id,metode_pembayaran,jumlah_bayar,status_pembayaran,tanggal_bayar,struk +1,4,ewallet-gopay,20000.0,sukses,2025-12-13 17:42:14, diff --git a/transaksi.csv b/transaksi.csv index dc3a07e..09be96f 100644 --- a/transaksi.csv +++ b/transaksi.csv @@ -1,2 +1,5 @@ id,user_id,nomor_meja,total,status,promo_code,subtotal,item_discount,promo_discount,tanggal 1,4,1,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 15:09:50 +2,4,1,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 16:17:53 +3,4,3,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 16:33:41 +4,4,2,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 17:22:52