diff --git a/project/__pycache__/admin_menu.cpython-313.pyc b/project/__pycache__/admin_menu.cpython-313.pyc index 7f3c35d..ebdbf09 100644 Binary files a/project/__pycache__/admin_menu.cpython-313.pyc and b/project/__pycache__/admin_menu.cpython-313.pyc differ diff --git a/project/__pycache__/database.cpython-313.pyc b/project/__pycache__/database.cpython-313.pyc index 3d5189d..89a55de 100644 Binary files a/project/__pycache__/database.cpython-313.pyc and b/project/__pycache__/database.cpython-313.pyc differ diff --git a/project/__pycache__/kasir.cpython-313.pyc b/project/__pycache__/kasir.cpython-313.pyc index d3ca0df..ae0c75b 100644 Binary files a/project/__pycache__/kasir.cpython-313.pyc and b/project/__pycache__/kasir.cpython-313.pyc differ diff --git a/project/__pycache__/login.cpython-313.pyc b/project/__pycache__/login.cpython-313.pyc index f1a27ea..10551a2 100644 Binary files a/project/__pycache__/login.cpython-313.pyc and b/project/__pycache__/login.cpython-313.pyc differ diff --git a/project/__pycache__/pembeli_menu.cpython-313.pyc b/project/__pycache__/pembeli_menu.cpython-313.pyc index ee4e315..7cdb9f4 100644 Binary files a/project/__pycache__/pembeli_menu.cpython-313.pyc and b/project/__pycache__/pembeli_menu.cpython-313.pyc differ diff --git a/project/__pycache__/pemilik.cpython-313.pyc b/project/__pycache__/pemilik.cpython-313.pyc index 8b813bd..cad7e83 100644 Binary files a/project/__pycache__/pemilik.cpython-313.pyc and b/project/__pycache__/pemilik.cpython-313.pyc differ diff --git a/project/__pycache__/waiter_dashboard.cpython-313.pyc b/project/__pycache__/waiter_dashboard.cpython-313.pyc index 6f6991b..7edf41b 100644 Binary files a/project/__pycache__/waiter_dashboard.cpython-313.pyc and b/project/__pycache__/waiter_dashboard.cpython-313.pyc differ diff --git a/project/admin_menu.py b/project/admin_menu.py index 44beca4..733207d 100644 --- a/project/admin_menu.py +++ b/project/admin_menu.py @@ -4,27 +4,21 @@ from database import connect import os import shutil -# GANTI NAMA CLASS JADI AdminPage -class AdminPage(tk.Frame): +class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage def __init__(self, parent, controller): super().__init__(parent) self.controller = controller self.selected_image_path = None - self.data_menu = [] # --- Header --- top = tk.Frame(self, bg="#333") top.pack(fill="x") tk.Label(top, text="ADMIN DASHBOARD", font=("Arial", 16, "bold"), fg="white", bg="#333").pack(side="left", padx=10, pady=10) - tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10) + tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: self.controller.show_frame("LoginPage")).pack(side="right", padx=10) - # --- Layout Kiri (Form) & Kanan (Tabel) --- - main_content = tk.Frame(self) - main_content.pack(fill="both", expand=True, padx=10, pady=10) - - # --- KIRI --- - left_frame = tk.Frame(main_content) - left_frame.pack(side="left", fill="y", padx=10) + # --- Form Input (Kiri) --- + left_frame = tk.Frame(self, padx=10, pady=10) + left_frame.pack(side="left", fill="y") tk.Label(left_frame, text="Nama Menu:").pack(anchor="w") self.entry_nama = tk.Entry(left_frame, width=30) @@ -36,7 +30,6 @@ class AdminPage(tk.Frame): tk.Label(left_frame, text="Stok Awal:").pack(anchor="w") self.entry_stok = tk.Entry(left_frame, width=30) - self.entry_stok.insert(0, "100") # Default stok self.entry_stok.pack(pady=5) tk.Label(left_frame, text="Gambar:").pack(anchor="w") @@ -45,16 +38,17 @@ class AdminPage(tk.Frame): self.lbl_img_path = tk.Label(left_frame, text="Belum ada gambar", fg="gray", font=("Arial", 8)) self.lbl_img_path.pack(anchor="w") + # Tombol Aksi btn_box = tk.Frame(left_frame, pady=20) btn_box.pack() tk.Button(btn_box, text="TAMBAH", bg="#4CAF50", fg="white", command=self.add_menu).grid(row=0, column=0, padx=5) tk.Button(btn_box, text="HAPUS", bg="#f44336", fg="white", command=self.delete_menu).grid(row=0, column=1, padx=5) - # --- KANAN --- - right_frame = tk.Frame(main_content) + # --- Tabel Menu (Kanan) --- + right_frame = tk.Frame(self, padx=10, pady=10) right_frame.pack(side="right", fill="both", expand=True) - tk.Label(right_frame, text="Daftar Menu:", font=("Arial", 10, "bold")).pack(anchor="w") + tk.Label(right_frame, text="Daftar Menu Database:", font=("Arial", 10, "bold")).pack(anchor="w") self.list_menu = tk.Listbox(right_frame) self.list_menu.pack(fill="both", expand=True) @@ -79,7 +73,7 @@ class AdminPage(tk.Frame): self.list_menu.insert(tk.END, f"{row[1]} - Rp {int(row[2])} (Stok: {row[3]})") def browse_image(self): - filename = filedialog.askopenfilename(title="Pilih Gambar", filetypes=[("Images", "*.png;*.jpg;*.jpeg")]) + filename = filedialog.askopenfilename(title="Pilih Gambar", filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")]) if filename: self.selected_image_path = filename self.lbl_img_path.config(text=os.path.basename(filename), fg="black") @@ -96,20 +90,21 @@ class AdminPage(tk.Frame): final_image_path = "default.png" if self.selected_image_path: if not os.path.exists("img"): os.makedirs("img") - destinasi = os.path.join("img", os.path.basename(self.selected_image_path)) - try: - shutil.copy(self.selected_image_path, destinasi) - final_image_path = destinasi - except: - pass + nama_file_asli = os.path.basename(self.selected_image_path) + destinasi = os.path.join("img", nama_file_asli) + shutil.copy(self.selected_image_path, destinasi) + final_image_path = destinasi db = connect() cur = db.cursor() try: cur.execute("INSERT INTO menu (nama, harga, stok, gambar) VALUES (?, ?, ?, ?)", - (nama, harga, stok, final_image_path)) + (nama, harga, stok if stok else 0, final_image_path)) db.commit() messagebox.showinfo("Sukses", "Menu berhasil ditambahkan!") + self.entry_nama.delete(0, tk.END) + self.entry_harga.delete(0, tk.END) + self.entry_stok.delete(0, tk.END) self.update_data() except Exception as e: messagebox.showerror("Error", str(e)) @@ -118,12 +113,14 @@ class AdminPage(tk.Frame): def delete_menu(self): idx = self.list_menu.curselection() - if not idx: return + if not idx: + messagebox.showwarning("Pilih", "Pilih menu dulu!") + return - selected = self.data_menu[idx[0]] - if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"): + id_menu = self.data_menu[idx[0]][0] + if messagebox.askyesno("Hapus", "Yakin hapus menu ini?"): db = connect() - db.cursor().execute("DELETE FROM menu WHERE id=?", (selected[0],)) + db.cursor().execute("DELETE FROM menu WHERE id=?", (id_menu,)) db.commit() db.close() self.update_data() \ No newline at end of file diff --git a/project/database.py b/project/database.py index fb9f26a..29c035c 100644 --- a/project/database.py +++ b/project/database.py @@ -1,4 +1,3 @@ -# File: database.py import sqlite3 def connect(): @@ -30,7 +29,7 @@ def setup_database(): ) """) - # 3. Transaksi + # 3. Transaksi (Header) cur.execute(""" CREATE TABLE IF NOT EXISTS transaksi( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -42,6 +41,7 @@ def setup_database(): status TEXT DEFAULT 'Pending' ) """) + # Status Flow: 'Pending' (Dipesan) -> 'Served' (Diantar Waiter) -> 'Paid' (Dibayar di Kasir) # 4. Detail Transaksi cur.execute(""" @@ -54,7 +54,7 @@ def setup_database(): ) """) - # Seeding User (Kalau kosong diisi default) + # Seeding Data User Dummy cur.execute("SELECT COUNT(*) FROM users") if cur.fetchone()[0] == 0: users = [ @@ -68,7 +68,7 @@ def setup_database(): db.commit() db.close() - print("Database Ready!") + print("Database Ready bos!") if __name__ == "__main__": setup_database() \ No newline at end of file diff --git a/project/isi_menu.py b/project/isi_menu.py index 4285925..cd3bd72 100644 --- a/project/isi_menu.py +++ b/project/isi_menu.py @@ -1,32 +1,49 @@ import sqlite3 +import os +# PERHATIKAN: Ini harus setup_database, BUKAN init_db +from database import setup_database def isi_data_awal(): - conn = sqlite3.connect("cafe.db") + # 1. Pastikan database dan tabel sudah dibuat + setup_database() + + db_name = "cafe.db" + + # 2. Cek folder aset + # Pastikan kamu punya folder bernama 'aset' dan di dalamnya ada gambar-gambarnya + if not os.path.exists("aset"): + print("❌ WADUH! Folder 'aset' gak ketemu. Pastikan nama folder di kiri adalah 'aset'.") + return + + conn = sqlite3.connect(db_name) cur = conn.cursor() - # Hapus data lama biar gak dobel (opsional, biar bersih) + print("♻️ Membersihkan data lama...") cur.execute("DELETE FROM menu") + cur.execute("DELETE FROM sqlite_sequence WHERE name='menu'") # Reset ID jadi 1 - # Data Menu (Sesuaikan nama file gambar dengan yang ada di folder aset kamu) + # 3. Data Menu (Path gambar pakai 'aset/') menus = [ - # (Nama, Kategori, Harga, Stok, Nama File Gambar) - ("Ayam Goreng", "Makanan", 15000, 20, "ayam_goreng.jpg"), - ("Bakso Urat", "Makanan", 12000, 15, "bakso.jpg"), - ("Mie Ayam", "Makanan", 10000, 25, "mie_ayam.jpg"), - ("Es Teh Manis", "Minuman", 3000, 50, "es_teh.jpg"), - ("Jus Jeruk", "Minuman", 5000, 30, "jus_jeruk.jpg"), + ("Ayam Goreng", "Makanan", 15000, 20, "aset/ayam_goreng.jpg"), + ("Bakso Urat", "Makanan", 12000, 15, "aset/bakso.jpg"), + ("Mie Ayam", "Makanan", 10000, 25, "aset/mie_ayam.jpg"), + ("Es Teh Manis", "Minuman", 3000, 50, "aset/es_teh.jpg"), + ("Jus Jeruk", "Minuman", 5000, 30, "aset/jus_jeruk.jpg"), ] - print("Sedang mengisi data menu...") + print("📥 Sedang mengisi data menu...") - cur.executemany(""" - INSERT INTO menu (nama, kategori, harga, stok, gambar) - VALUES (?, ?, ?, ?, ?) - """, menus) - - conn.commit() - conn.close() - print("✅ Berhasil! Data menu sudah masuk database.") + try: + cur.executemany(""" + INSERT INTO menu (nama, kategori, harga, stok, gambar) + VALUES (?, ?, ?, ?, ?) + """, menus) + conn.commit() + print("✅ MANTAP! Data menu berhasil masuk. Sekarang jalankan main.py!") + except Exception as e: + print(f"❌ Error: {e}") + finally: + conn.close() if __name__ == "__main__": isi_data_awal() \ No newline at end of file diff --git a/project/kasir.py b/project/kasir.py index 44906f5..37c0502 100644 --- a/project/kasir.py +++ b/project/kasir.py @@ -13,13 +13,13 @@ class KasirPage(tk.Frame): tk.Button(top, text="Logout", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10, pady=5) tk.Label(top, text="KASIR - PEMBAYARAN", font=("Arial", 16, "bold"), bg="#ddd").pack(side="left", padx=10) - # List Order - tk.Label(self, text="Tagihan Belum Lunas (Status: Served):", font=("Arial", 11)).pack(pady=10) - self.order_list = tk.Listbox(self, width=80, height=15, font=("Arial", 10)) + # List + tk.Label(self, text="Tagihan Belum Bayar (Status: Served):", font=("Arial", 11)).pack(pady=10) + self.order_list = tk.Listbox(self, width=80, height=15) self.order_list.pack(pady=5) tk.Button(self, text="Refresh Data", command=self.update_data).pack(pady=5) - tk.Button(self, text="💰 PROSES BAYAR (LUNAS)", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=50, pady=20) + tk.Button(self, text="💰 PROSES BAYAR & LUNAS", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=20, pady=20) def update_data(self): self.order_list.delete(0, tk.END) @@ -27,35 +27,36 @@ class KasirPage(tk.Frame): db = connect() cur = db.cursor() - # Kasir melihat yang statusnya Served + # Kasir melihat yang sudah diantar (Served) tapi belum bayar cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Served'") self.data_orders = cur.fetchall() db.close() for item in self.data_orders: - self.order_list.insert(tk.END, f"ID: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3])}") + self.order_list.insert(tk.END, f"ID Transaksi: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3]):,}") def bayar(self): idx = self.order_list.curselection() if not idx: - messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!") + messagebox.showwarning("Pilih", "Pilih tagihan dulu!") return selected = self.data_orders[idx[0]] - transaksi_id = selected[0] - nama = selected[1] + trans_id = selected[0] total = selected[3] - confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?") - if confirm: - db = connect() - cur = db.cursor() - - # Update status jadi 'Paid' dan metode pembayaran (misal Cash default) - cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='Cash' WHERE id=?", (transaksi_id,)) - - db.commit() - db.close() + # Konfirmasi + if not messagebox.askyesno("Konfirmasi", f"Terima pembayaran sebesar Rp {int(total):,}?"): + return - messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!") - self.update_data() \ No newline at end of file + db = connect() + cur = db.cursor() + + # Update status jadi 'Paid' + cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='CASH' WHERE id=?", (trans_id,)) + + db.commit() + db.close() + + messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!") + self.update_data() \ No newline at end of file diff --git a/project/login.py b/project/login.py index 937b191..91f9742 100644 --- a/project/login.py +++ b/project/login.py @@ -2,52 +2,57 @@ import tkinter as tk from tkinter import messagebox from database import connect +# Pastikan import ini sesuai +from admin_menu import AdminPage +from pembeli_menu import PembeliPage +from kasir import KasirPage +from pemilik import PemilikPage +from waiter_dashboard import WaiterPage + class LoginPage(tk.Frame): def __init__(self, parent, controller): super().__init__(parent) self.controller = controller - # Center Content - center_frame = tk.Frame(self) - center_frame.place(relx=0.5, rely=0.5, anchor="center") + tk.Label(self, text="LOGIN SYSTEM", font=("Arial", 20, "bold")).pack(pady=40) - tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20) + frame_login = tk.Frame(self) + frame_login.pack() - tk.Label(center_frame, text="Username").pack(anchor="w") - self.user = tk.Entry(center_frame, width=30) + tk.Label(frame_login, text="Username:").pack() + self.user = tk.Entry(frame_login) self.user.pack(pady=5) - tk.Label(center_frame, text="Password").pack(anchor="w") - self.passw = tk.Entry(center_frame, show="*", width=30) + tk.Label(frame_login, text="Password:").pack() + self.passw = tk.Entry(frame_login, show="*") self.passw.pack(pady=5) - tk.Button(center_frame, text="Login", bg="#4CAF50", fg="white", width=20, command=self.login).pack(pady=20) + tk.Button(frame_login, text="LOGIN", command=self.login, width=20, bg="#2196F3", fg="white").pack(pady=20) def login(self): - username = self.user.get() - password = self.passw.get() + u = self.user.get() + p = self.passw.get() db = connect() cur = db.cursor() - cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username, password)) + cur.execute("SELECT role FROM users WHERE username=? AND password=?", (u, p)) res = cur.fetchone() db.close() if not res: - messagebox.showerror("Error", "Username/Password salah!") + messagebox.showerror("Gagal", "Username/Password salah!") return - - role = res[0] + role = res[0] # Reset input self.user.delete(0, tk.END) self.passw.delete(0, tk.END) - # Arahkan sesuai Role (Nama Class harus sama dengan di main.py) + # Redirect sesuai Role if role == "admin": self.controller.show_frame("AdminPage") elif role == "pembeli": - self.controller.show_frame("PembeliMenu") + self.controller.show_frame("PembeliPage") elif role == "kasir": self.controller.show_frame("KasirPage") elif role == "pemilik": diff --git a/project/main.py b/project/main.py index 232b2dd..a27e813 100644 --- a/project/main.py +++ b/project/main.py @@ -1,10 +1,10 @@ import tkinter as tk from database import setup_database -# Pastikan nama file dan nama class sesuai +# Import class yang sudah kita benerin namanya from login import LoginPage -from admin_menu import AdminPage # Class diganti jadi AdminPage di file admin_menu -from pembeli_menu import PembeliMenu # Class tetap PembeliMenu +from admin_menu import AdminPage +from pembeli_menu import PembeliPage from kasir import KasirPage from pemilik import PemilikPage from waiter_dashboard import WaiterPage @@ -12,10 +12,10 @@ from waiter_dashboard import WaiterPage class CafeApp(tk.Tk): def __init__(self): super().__init__() - self.title("Sistem Manajemen Kafe") + self.title("Sistem Manajemen Kafe Python") self.geometry("1000x700") - setup_database() + setup_database() # Auto bikin tabel self.container = tk.Frame(self) self.container.pack(side="top", fill="both", expand=True) @@ -24,8 +24,8 @@ class CafeApp(tk.Tk): self.frames = {} - # Loop semua class halaman - for F in (LoginPage, AdminPage, PembeliMenu, KasirPage, PemilikPage, WaiterPage): + # Loop semua halaman + for F in (LoginPage, AdminPage, PembeliPage, KasirPage, PemilikPage, WaiterPage): page_name = F.__name__ frame = F(parent=self.container, controller=self) self.frames[page_name] = frame diff --git a/project/pembeli_menu.py b/project/pembeli_menu.py index 767a570..5b9d7e3 100644 --- a/project/pembeli_menu.py +++ b/project/pembeli_menu.py @@ -1,177 +1,169 @@ import tkinter as tk -from tkinter import messagebox, simpledialog -from PIL import Image, ImageTk -import os +from tkinter import messagebox, ttk from database import connect -class PembeliMenu(tk.Frame): +class PembeliPage(tk.Frame): # Ganti nama class jadi PembeliPage def __init__(self, parent, controller): super().__init__(parent) self.controller = controller - self.keranjang = [] # List untuk simpan belanjaan sementara - self.image_refs = [] # Supaya gambar tidak hilang + self.cart = {} + self.nama_pelanggan = "" + self.no_meja = "" - # --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) --- + self.login_frame = tk.Frame(self) + self.menu_frame = tk.Frame(self) + self.show_login_ui() + + # --- UI 1: LOGIN PEMBELI --- + def show_login_ui(self): + self.menu_frame.pack_forget() + self.login_frame.pack(fill="both", expand=True) - # 1. Frame Kiri (Daftar Menu) - self.left_frame = tk.Frame(self) - self.left_frame.pack(side="left", fill="both", expand=True) + for w in self.login_frame.winfo_children(): w.destroy() - # Header Kiri - header = tk.Frame(self.left_frame, bg="#2c3e50", height=50) - header.pack(fill="x") - tk.Label(header, text="DAFTAR MENU", font=("Arial", 14, "bold"), fg="white", bg="#2c3e50").pack(pady=10) + tk.Label(self.login_frame, text="SELAMAT DATANG", font=("Arial", 20, "bold")).pack(pady=50) + + box = tk.Frame(self.login_frame, bd=2, relief="groove", padx=20, pady=20) + box.pack() - # Canvas untuk Scroll Menu - self.canvas = tk.Canvas(self.left_frame) - self.scrollbar = tk.Scrollbar(self.left_frame, orient="vertical", command=self.canvas.yview) - self.scrollable_frame = tk.Frame(self.canvas) + tk.Label(box, text="Nama Anda:").pack(anchor="w") + self.entry_nama = tk.Entry(box, width=30) + self.entry_nama.pack(pady=5) - self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) - self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") - self.canvas.configure(yscrollcommand=self.scrollbar.set) + tk.Label(box, text="Nomor Meja:").pack(anchor="w") + self.combo_meja = ttk.Combobox(box, values=[str(i) for i in range(1, 21)], state="readonly") + self.combo_meja.current(0) + self.combo_meja.pack(pady=5) - self.canvas.pack(side="left", fill="both", expand=True) - self.scrollbar.pack(side="right", fill="y") + tk.Button(box, text="MULAI PESAN", bg="#4CAF50", fg="white", command=self.validate_login).pack(pady=20, fill="x") + tk.Button(self.login_frame, text="Kembali", command=lambda: self.controller.show_frame("LoginPage")).pack(pady=10) - # 2. Frame Kanan (Keranjang Belanja) - self.right_frame = tk.Frame(self, bg="#ecf0f1", width=300) - self.right_frame.pack(side="right", fill="y") - self.right_frame.pack_propagate(False) # Agar lebar tetap 300px + def validate_login(self): + if not self.entry_nama.get(): + messagebox.showwarning("Oops", "Nama wajib diisi!") + return + self.nama_pelanggan = self.entry_nama.get() + self.no_meja = self.combo_meja.get() + self.show_menu_ui() - tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x") + # --- UI 2: MENU MAKANAN --- + def show_menu_ui(self): + self.login_frame.pack_forget() + self.menu_frame.pack(fill="both", expand=True) + + # Bersihkan frame lama biar gak numpuk + for w in self.menu_frame.winfo_children(): w.destroy() - # Listbox Keranjang - self.cart_listbox = tk.Listbox(self.right_frame, font=("Arial", 10)) - self.cart_listbox.pack(fill="both", expand=True, padx=10, pady=10) + # Header + top = tk.Frame(self.menu_frame, bg="#eee", pady=10) + top.pack(fill="x") + tk.Label(top, text=f"👤 {self.nama_pelanggan} | Meja {self.no_meja}", font=("Arial", 12)).pack(side="left", padx=20) + tk.Button(top, text="Keluar", bg="#ff6b6b", fg="white", command=self.logout).pack(side="right", padx=20) - # Label Total Harga - self.total_label = tk.Label(self.right_frame, text="Total: Rp 0", font=("Arial", 14, "bold"), bg="#ecf0f1", fg="#e74c3c") - self.total_label.pack(pady=10) + content = tk.Frame(self.menu_frame) + content.pack(fill="both", expand=True, padx=10, pady=10) - # Tombol Aksi - tk.Button(self.right_frame, text="Hapus Item Terpilih", bg="#e67e22", fg="white", command=self.hapus_item).pack(fill="x", padx=10, pady=5) - tk.Button(self.right_frame, text="CHECKOUT / BAYAR", bg="#27ae60", fg="white", font=("Arial", 10, "bold"), height=2, command=self.checkout).pack(fill="x", padx=10, pady=20) - tk.Button(self.right_frame, text="Kembali / Logout", command=lambda: controller.show_frame("LoginPage")).pack(pady=5) + # Kiri: Daftar Menu + left_frame = tk.LabelFrame(content, text=" Menu Tersedia ") + left_frame.pack(side="left", fill="both", expand=True) - def update_data(self): - """Dipanggil saat halaman dibuka""" - self.load_menu() - self.keranjang = [] # Reset keranjang tiap login baru - self.update_keranjang_ui() + canvas = tk.Canvas(left_frame) + scrollbar = tk.Scrollbar(left_frame, orient="vertical", command=canvas.yview) + self.scroll_frame = tk.Frame(canvas) - def load_menu(self): - # Bersihkan area menu - for widget in self.scrollable_frame.winfo_children(): - widget.destroy() - self.image_refs.clear() + self.scroll_frame.bind("", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) + canvas.create_window((0, 0), window=self.scroll_frame, anchor="nw") + canvas.configure(yscrollcommand=scrollbar.set) + + canvas.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + # Kanan: Keranjang + right_frame = tk.LabelFrame(content, text=" Keranjang ") + right_frame.pack(side="right", fill="both", width=300, padx=5) + + self.cart_list = tk.Listbox(right_frame) + self.cart_list.pack(fill="both", expand=True, padx=5, pady=5) + + self.lbl_total = tk.Label(right_frame, text="Total: Rp 0", font=("Arial", 14, "bold"), fg="red") + self.lbl_total.pack(pady=5) + + tk.Button(right_frame, text="PESAN SEKARANG", bg="green", fg="white", height=2, command=self.checkout).pack(fill="x", padx=5, pady=5) + + self.load_menu_items() + + def load_menu_items(self): + for w in self.scroll_frame.winfo_children(): w.destroy() + db = connect() cur = db.cursor() - cur.execute("SELECT * FROM menu") + cur.execute("SELECT id, nama, harga, stok FROM menu WHERE stok > 0") items = cur.fetchall() db.close() - columns = 3 - for index, item in enumerate(items): - self.create_card(item, index, columns) + row, col = 0, 0 + for item in items: + m_id, nama, harga, stok = item + card = tk.Frame(self.scroll_frame, bd=2, relief="ridge", padx=5, pady=5) + card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") + + tk.Label(card, text=nama, font=("Arial", 10, "bold")).pack() + tk.Label(card, text=f"Rp {int(harga):,}", fg="green").pack() + tk.Button(card, text="Tambah +", command=lambda i=m_id, n=nama, h=harga: self.add_to_cart(i, n, h)).pack(pady=2) - def create_card(self, item, index, columns): - id_menu, nama, kategori, harga, stok, file_gambar = item - row = index // columns - col = index % columns + col += 1 + if col > 2: + col = 0 + row += 1 - card = tk.Frame(self.scrollable_frame, bd=2, relief="groove", bg="white") - card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") + def add_to_cart(self, m_id, nama, harga): + if m_id in self.cart: + self.cart[m_id]['qty'] += 1 + else: + self.cart[m_id] = {'nama': nama, 'harga': harga, 'qty': 1} + self.refresh_cart() - # Load Gambar - path = os.path.join("aset", file_gambar) - try: - img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS) - photo = ImageTk.PhotoImage(img) - self.image_refs.append(photo) - tk.Label(card, image=photo, bg="white").pack(pady=5) - except: - tk.Label(card, text="[No Image]", bg="#eee", height=4).pack(pady=5) - - tk.Label(card, text=nama, font=("Arial", 10, "bold"), bg="white").pack() - tk.Label(card, text=f"Rp {int(harga):,}", fg="green", bg="white").pack() - - # Tombol Tambah - state = "normal" if stok > 0 else "disabled" - text_btn = "Tambah" if stok > 0 else "Habis" - tk.Button(card, text=text_btn, bg="#3498db", fg="white", state=state, - command=lambda: self.tambah_ke_keranjang(item)).pack(pady=5, padx=5, fill="x") - - def tambah_ke_keranjang(self, item): - # item = (id, nama, kategori, harga, stok, gambar) - self.keranjang.append(item) - self.update_keranjang_ui() - - def update_keranjang_ui(self): - self.cart_listbox.delete(0, tk.END) + def refresh_cart(self): + self.cart_list.delete(0, tk.END) total = 0 - for item in self.keranjang: - nama = item[1] - harga = item[3] - self.cart_listbox.insert(tk.END, f"{nama} - Rp {int(harga):,}") - total += harga - - self.total_label.config(text=f"Total: Rp {int(total):,}") - - def hapus_item(self): - selected = self.cart_listbox.curselection() - if not selected: - return - index = selected[0] - del self.keranjang[index] - self.update_keranjang_ui() + for m_id, item in self.cart.items(): + sub = item['harga'] * item['qty'] + total += sub + self.cart_list.insert(tk.END, f"{item['qty']}x {item['nama']} = {sub:,}") + self.lbl_total.config(text=f"Total: Rp {int(total):,}") def checkout(self): - if not self.keranjang: - messagebox.showwarning("Kosong", "Keranjang masih kosong!") - return - - nama_pelanggan = simpledialog.askstring("Input", "Masukkan Nama Pelanggan:") - if not nama_pelanggan: return - - total_harga = sum(item[3] for item in self.keranjang) + if not self.cart: return + + total_val = int(self.lbl_total.cget("text").replace("Total: Rp ","").replace(",","")) - # Simpan ke Database db = connect() cur = db.cursor() - - try: - # 1. Simpan Transaksi Utama - cur.execute(""" - INSERT INTO transaksi (nama_pelanggan, total, status) - VALUES (?, ?, 'Pending') - """, (nama_pelanggan, total_harga)) - transaksi_id = cur.lastrowid - - # 2. Simpan Detail Transaksi (Looping item di keranjang) - for item in self.keranjang: - menu_id = item[0] - harga = item[3] - # Masukkan ke detail - cur.execute(""" - INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal) - VALUES (?, ?, 1, ?) - """, (transaksi_id, menu_id, harga)) - - # Kurangi Stok Menu - cur.execute("UPDATE menu SET stok = stok - 1 WHERE id = ?", (menu_id,)) - - db.commit() - messagebox.showinfo("Berhasil", "Pesanan berhasil dibuat! Silakan bayar di kasir.") + + # Simpan ke tabel TRANSAKSI (Bukan orders) + cur.execute("INSERT INTO transaksi (nama_pelanggan, meja_id, total, status) VALUES (?, ?, ?, 'Pending')", + (self.nama_pelanggan, self.no_meja, total_val)) + trans_id = cur.lastrowid + + # Simpan Detail + for m_id, item in self.cart.items(): + sub = item['harga'] * item['qty'] + cur.execute("INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal) VALUES (?,?,?,?)", + (trans_id, m_id, item['qty'], sub)) + # Kurangi Stok + cur.execute("UPDATE menu SET stok = stok - ? WHERE id=?", (item['qty'], m_id)) - # Reset - self.keranjang = [] - self.update_keranjang_ui() - self.load_menu() # Reload menu untuk update stok visual + db.commit() + db.close() + + messagebox.showinfo("Sukses", "Pesanan terkirim! Mohon tunggu.") + self.cart = {} + self.refresh_cart() + self.load_menu_items() - except Exception as e: - db.rollback() - messagebox.showerror("Error", f"Gagal Checkout: {e}") - finally: - db.close() \ No newline at end of file + def logout(self): + self.cart = {} + self.show_login_ui() + + def update_data(self): + self.show_login_ui() \ No newline at end of file diff --git a/project/pemilik.py b/project/pemilik.py index 5942a30..f438cf9 100644 --- a/project/pemilik.py +++ b/project/pemilik.py @@ -1,43 +1,36 @@ import tkinter as tk from database import connect -class PemilikPage(tk.Frame): +class PemilikPage(tk.Frame): # HARUS inherit tk.Frame def __init__(self, parent, controller): - super().__init__(parent) + super().__init__(parent) # Init parent class self.controller = controller - # --- PERBAIKAN: Baris self.pack() sudah DIHAPUS --- - # Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid. + tk.Label(self, text="LAPORAN PENJUALAN (OWNER)", font=("Arial", 18, "bold")).pack(pady=20) - # Judul Halaman - tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20) + self.box = tk.Frame(self, bd=2, relief="groove", padx=40, pady=40) + self.box.pack() # Label Total - self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green") - self.total_lbl.pack(pady=20) - - tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack() + self.total_lbl = tk.Label(self.box, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green") + self.total_lbl.pack(pady=10) - # Tombol-tombol - tk.Button(self, text="Refresh Laporan", bg="#cfe2ff", command=self.load).pack(pady=10) - tk.Button(self, text="Logout", bg="#f9e79f", command=lambda: controller.show_frame("LoginPage")).pack(pady=10) + self.count_lbl = tk.Label(self.box, text="Jumlah Transaksi: 0", font=("Arial", 12)) + self.count_lbl.pack(pady=5) + + tk.Button(self, text="Refresh Laporan", bg="#cfe2ff", command=self.update_data).pack(pady=10) + tk.Button(self, text="Logout", bg="#f9e79f", command=lambda: controller.show_frame("LoginPage")).pack(pady=5) def update_data(self): - """Dipanggil otomatis saat halaman dibuka""" - self.load() - - def load(self): - """Ambil data total penjualan dari database""" db = connect() cur = db.cursor() - - # Hitung sum total dari transaksi yang sudah Paid (Lunas) - cur.execute("SELECT SUM(total) FROM transaksi WHERE status='Paid'") - result = cur.fetchone()[0] - - # Kalau belum ada penjualan, set 0 - total = result if result else 0 + # Hitung total dari transaksi yang statusnya 'Paid' + cur.execute("SELECT SUM(total), COUNT(id) FROM transaksi WHERE status='Paid'") + res = cur.fetchone() db.close() - # Update tampilan - self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}") \ No newline at end of file + total_duit = res[0] if res[0] else 0 + total_transaksi = res[1] if res[1] else 0 + + self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total_duit):,}") + self.count_lbl.config(text=f"Jumlah Transaksi Sukses: {total_transaksi}") \ No newline at end of file diff --git a/project/waiter_dashboard.py b/project/waiter_dashboard.py index bec7dd7..8a95d0e 100644 --- a/project/waiter_dashboard.py +++ b/project/waiter_dashboard.py @@ -13,16 +13,16 @@ class WaiterPage(tk.Frame): tk.Label(top, text="WAITER DASHBOARD", font=("Arial", 16, "bold"), bg="#FF9800", fg="white").pack(side="left", padx=10, pady=10) tk.Button(top, text="Logout", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10) - # List Pesanan - tk.Label(self, text="Pesanan Status: PENDING (Perlu Diantar)", font=("Arial", 12, "bold")).pack(pady=10) + # Content + tk.Label(self, text="Pesanan Pending (Perlu Diantar):", font=("Arial", 12)).pack(pady=10) - self.list_orders = tk.Listbox(self, width=80, height=15, font=("Arial", 10)) + self.list_orders = tk.Listbox(self, width=80, height=15) self.list_orders.pack(pady=5) btn_frame = tk.Frame(self) btn_frame.pack(pady=10) - tk.Button(btn_frame, text="Refresh Data", command=self.update_data).pack(side="left", padx=5) + tk.Button(btn_frame, text="Refresh", command=self.update_data).pack(side="left", padx=5) tk.Button(btn_frame, text="✅ Selesai Diantar (Served)", bg="#81C784", command=self.mark_served).pack(side="left", padx=5) def update_data(self): @@ -31,14 +31,14 @@ class WaiterPage(tk.Frame): db = connect() cur = db.cursor() - # Ambil Transaksi yang statusnya Pending + # Ambil transaksi yang statusnya 'Pending' cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Pending'") self.data_orders = cur.fetchall() db.close() for item in self.data_orders: - # item: (id, nama, meja, total) - self.list_orders.insert(tk.END, f"ID Transaksi: {item[0]} | Meja: {item[2]} | A.N: {item[1]} | Total: Rp {int(item[3])}") + # item = (id, nama, meja, total) + self.list_orders.insert(tk.END, f"ID: {item[0]} | Meja: {item[2]} | A.N: {item[1]} | Total: Rp {int(item[3]):,}") def mark_served(self): idx = self.list_orders.curselection() @@ -46,15 +46,14 @@ class WaiterPage(tk.Frame): messagebox.showwarning("Pilih", "Pilih pesanan dulu!") return - selected = self.data_orders[idx[0]] - transaksi_id = selected[0] + trans_id = self.data_orders[idx[0]][0] db = connect() cur = db.cursor() - # Update Status Transaksi jadi Served - cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (transaksi_id,)) + # Update status jadi 'Served' biar muncul di Kasir + cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (trans_id,)) db.commit() db.close() - messagebox.showinfo("Sukses", "Pesanan Meja selesai dilayani!") + messagebox.showinfo("Sukses", "Pesanan Meja ini sudah dilayani!") self.update_data() \ No newline at end of file