This commit is contained in:
Nathan 2025-12-15 00:05:18 +07:00
commit 2d2669361d
16 changed files with 278 additions and 274 deletions

View File

@ -4,27 +4,21 @@ from database import connect
import os import os
import shutil import shutil
# GANTI NAMA CLASS JADI AdminPage class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
class AdminPage(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) super().__init__(parent)
self.controller = controller self.controller = controller
self.selected_image_path = None self.selected_image_path = None
self.data_menu = []
# --- Header --- # --- Header ---
top = tk.Frame(self, bg="#333") top = tk.Frame(self, bg="#333")
top.pack(fill="x") 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.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) --- # --- Form Input (Kiri) ---
main_content = tk.Frame(self) left_frame = tk.Frame(self, padx=10, pady=10)
main_content.pack(fill="both", expand=True, padx=10, pady=10) left_frame.pack(side="left", fill="y")
# --- KIRI ---
left_frame = tk.Frame(main_content)
left_frame.pack(side="left", fill="y", padx=10)
tk.Label(left_frame, text="Nama Menu:").pack(anchor="w") tk.Label(left_frame, text="Nama Menu:").pack(anchor="w")
self.entry_nama = tk.Entry(left_frame, width=30) 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") tk.Label(left_frame, text="Stok Awal:").pack(anchor="w")
self.entry_stok = tk.Entry(left_frame, width=30) self.entry_stok = tk.Entry(left_frame, width=30)
self.entry_stok.insert(0, "100") # Default stok
self.entry_stok.pack(pady=5) self.entry_stok.pack(pady=5)
tk.Label(left_frame, text="Gambar:").pack(anchor="w") 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 = tk.Label(left_frame, text="Belum ada gambar", fg="gray", font=("Arial", 8))
self.lbl_img_path.pack(anchor="w") self.lbl_img_path.pack(anchor="w")
# Tombol Aksi
btn_box = tk.Frame(left_frame, pady=20) btn_box = tk.Frame(left_frame, pady=20)
btn_box.pack() 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="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) tk.Button(btn_box, text="HAPUS", bg="#f44336", fg="white", command=self.delete_menu).grid(row=0, column=1, padx=5)
# --- KANAN --- # --- Tabel Menu (Kanan) ---
right_frame = tk.Frame(main_content) right_frame = tk.Frame(self, padx=10, pady=10)
right_frame.pack(side="right", fill="both", expand=True) 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 = tk.Listbox(right_frame)
self.list_menu.pack(fill="both", expand=True) 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]})") self.list_menu.insert(tk.END, f"{row[1]} - Rp {int(row[2])} (Stok: {row[3]})")
def browse_image(self): 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: if filename:
self.selected_image_path = filename self.selected_image_path = filename
self.lbl_img_path.config(text=os.path.basename(filename), fg="black") 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" final_image_path = "default.png"
if self.selected_image_path: if self.selected_image_path:
if not os.path.exists("img"): os.makedirs("img") if not os.path.exists("img"): os.makedirs("img")
destinasi = os.path.join("img", os.path.basename(self.selected_image_path)) nama_file_asli = os.path.basename(self.selected_image_path)
try: destinasi = os.path.join("img", nama_file_asli)
shutil.copy(self.selected_image_path, destinasi) shutil.copy(self.selected_image_path, destinasi)
final_image_path = destinasi final_image_path = destinasi
except:
pass
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
try: try:
cur.execute("INSERT INTO menu (nama, harga, stok, gambar) VALUES (?, ?, ?, ?)", 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() db.commit()
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan!") 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() self.update_data()
except Exception as e: except Exception as e:
messagebox.showerror("Error", str(e)) messagebox.showerror("Error", str(e))
@ -118,12 +113,14 @@ class AdminPage(tk.Frame):
def delete_menu(self): def delete_menu(self):
idx = self.list_menu.curselection() 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]] id_menu = self.data_menu[idx[0]][0]
if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"): if messagebox.askyesno("Hapus", "Yakin hapus menu ini?"):
db = connect() 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.commit()
db.close() db.close()
self.update_data() self.update_data()

View File

@ -1,4 +1,3 @@
# File: database.py
import sqlite3 import sqlite3
def connect(): def connect():
@ -30,7 +29,7 @@ def setup_database():
) )
""") """)
# 3. Transaksi # 3. Transaksi (Header)
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS transaksi( CREATE TABLE IF NOT EXISTS transaksi(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -42,6 +41,7 @@ def setup_database():
status TEXT DEFAULT 'Pending' status TEXT DEFAULT 'Pending'
) )
""") """)
# Status Flow: 'Pending' (Dipesan) -> 'Served' (Diantar Waiter) -> 'Paid' (Dibayar di Kasir)
# 4. Detail Transaksi # 4. Detail Transaksi
cur.execute(""" 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") cur.execute("SELECT COUNT(*) FROM users")
if cur.fetchone()[0] == 0: if cur.fetchone()[0] == 0:
users = [ users = [
@ -68,7 +68,7 @@ def setup_database():
db.commit() db.commit()
db.close() db.close()
print("Database Ready!") print("Database Ready bos!")
if __name__ == "__main__": if __name__ == "__main__":
setup_database() setup_database()

View File

@ -1,32 +1,49 @@
import sqlite3 import sqlite3
import os
# PERHATIKAN: Ini harus setup_database, BUKAN init_db
from database import setup_database
def isi_data_awal(): 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() 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 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 = [ menus = [
# (Nama, Kategori, Harga, Stok, Nama File Gambar) ("Ayam Goreng", "Makanan", 15000, 20, "aset/ayam_goreng.jpg"),
("Ayam Goreng", "Makanan", 15000, 20, "ayam_goreng.jpg"), ("Bakso Urat", "Makanan", 12000, 15, "aset/bakso.jpg"),
("Bakso Urat", "Makanan", 12000, 15, "bakso.jpg"), ("Mie Ayam", "Makanan", 10000, 25, "aset/mie_ayam.jpg"),
("Mie Ayam", "Makanan", 10000, 25, "mie_ayam.jpg"), ("Es Teh Manis", "Minuman", 3000, 50, "aset/es_teh.jpg"),
("Es Teh Manis", "Minuman", 3000, 50, "es_teh.jpg"), ("Jus Jeruk", "Minuman", 5000, 30, "aset/jus_jeruk.jpg"),
("Jus Jeruk", "Minuman", 5000, 30, "jus_jeruk.jpg"),
] ]
print("Sedang mengisi data menu...") print("📥 Sedang mengisi data menu...")
cur.executemany(""" try:
INSERT INTO menu (nama, kategori, harga, stok, gambar) cur.executemany("""
VALUES (?, ?, ?, ?, ?) INSERT INTO menu (nama, kategori, harga, stok, gambar)
""", menus) VALUES (?, ?, ?, ?, ?)
""", menus)
conn.commit() conn.commit()
conn.close() print("✅ MANTAP! Data menu berhasil masuk. Sekarang jalankan main.py!")
print("✅ Berhasil! Data menu sudah masuk database.") except Exception as e:
print(f"❌ Error: {e}")
finally:
conn.close()
if __name__ == "__main__": if __name__ == "__main__":
isi_data_awal() isi_data_awal()

View File

@ -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.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) tk.Label(top, text="KASIR - PEMBAYARAN", font=("Arial", 16, "bold"), bg="#ddd").pack(side="left", padx=10)
# List Order # List
tk.Label(self, text="Tagihan Belum Lunas (Status: Served):", font=("Arial", 11)).pack(pady=10) 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, font=("Arial", 10)) self.order_list = tk.Listbox(self, width=80, height=15)
self.order_list.pack(pady=5) self.order_list.pack(pady=5)
tk.Button(self, text="Refresh Data", command=self.update_data).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): def update_data(self):
self.order_list.delete(0, tk.END) self.order_list.delete(0, tk.END)
@ -27,35 +27,36 @@ class KasirPage(tk.Frame):
db = connect() db = connect()
cur = db.cursor() 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'") cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Served'")
self.data_orders = cur.fetchall() self.data_orders = cur.fetchall()
db.close() db.close()
for item in self.data_orders: 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): def bayar(self):
idx = self.order_list.curselection() idx = self.order_list.curselection()
if not idx: if not idx:
messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!") messagebox.showwarning("Pilih", "Pilih tagihan dulu!")
return return
selected = self.data_orders[idx[0]] selected = self.data_orders[idx[0]]
transaksi_id = selected[0] trans_id = selected[0]
nama = selected[1]
total = selected[3] total = selected[3]
confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?") # Konfirmasi
if confirm: if not messagebox.askyesno("Konfirmasi", f"Terima pembayaran sebesar Rp {int(total):,}?"):
db = connect() return
cur = db.cursor()
# Update status jadi 'Paid' dan metode pembayaran (misal Cash default) db = connect()
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='Cash' WHERE id=?", (transaksi_id,)) cur = db.cursor()
db.commit() # Update status jadi 'Paid'
db.close() cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='CASH' WHERE id=?", (trans_id,))
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!") db.commit()
self.update_data() db.close()
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
self.update_data()

View File

@ -2,52 +2,57 @@ import tkinter as tk
from tkinter import messagebox from tkinter import messagebox
from database import connect 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): class LoginPage(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) super().__init__(parent)
self.controller = controller self.controller = controller
# Center Content tk.Label(self, text="LOGIN SYSTEM", font=("Arial", 20, "bold")).pack(pady=40)
center_frame = tk.Frame(self)
center_frame.place(relx=0.5, rely=0.5, anchor="center")
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") tk.Label(frame_login, text="Username:").pack()
self.user = tk.Entry(center_frame, width=30) self.user = tk.Entry(frame_login)
self.user.pack(pady=5) self.user.pack(pady=5)
tk.Label(center_frame, text="Password").pack(anchor="w") tk.Label(frame_login, text="Password:").pack()
self.passw = tk.Entry(center_frame, show="*", width=30) self.passw = tk.Entry(frame_login, show="*")
self.passw.pack(pady=5) 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): def login(self):
username = self.user.get() u = self.user.get()
password = self.passw.get() p = self.passw.get()
db = connect() db = connect()
cur = db.cursor() 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() res = cur.fetchone()
db.close() db.close()
if not res: if not res:
messagebox.showerror("Error", "Username/Password salah!") messagebox.showerror("Gagal", "Username/Password salah!")
return return
role = res[0] role = res[0]
# Reset input # Reset input
self.user.delete(0, tk.END) self.user.delete(0, tk.END)
self.passw.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": if role == "admin":
self.controller.show_frame("AdminPage") self.controller.show_frame("AdminPage")
elif role == "pembeli": elif role == "pembeli":
self.controller.show_frame("PembeliMenu") self.controller.show_frame("PembeliPage")
elif role == "kasir": elif role == "kasir":
self.controller.show_frame("KasirPage") self.controller.show_frame("KasirPage")
elif role == "pemilik": elif role == "pemilik":

View File

@ -1,10 +1,10 @@
import tkinter as tk import tkinter as tk
from database import setup_database from database import setup_database
# Pastikan nama file dan nama class sesuai # Import class yang sudah kita benerin namanya
from login import LoginPage from login import LoginPage
from admin_menu import AdminPage # Class diganti jadi AdminPage di file admin_menu from admin_menu import AdminPage
from pembeli_menu import PembeliMenu # Class tetap PembeliMenu from pembeli_menu import PembeliPage
from kasir import KasirPage from kasir import KasirPage
from pemilik import PemilikPage from pemilik import PemilikPage
from waiter_dashboard import WaiterPage from waiter_dashboard import WaiterPage
@ -12,10 +12,10 @@ from waiter_dashboard import WaiterPage
class CafeApp(tk.Tk): class CafeApp(tk.Tk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("Sistem Manajemen Kafe") self.title("Sistem Manajemen Kafe Python")
self.geometry("1000x700") self.geometry("1000x700")
setup_database() setup_database() # Auto bikin tabel
self.container = tk.Frame(self) self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True) self.container.pack(side="top", fill="both", expand=True)
@ -24,8 +24,8 @@ class CafeApp(tk.Tk):
self.frames = {} self.frames = {}
# Loop semua class halaman # Loop semua halaman
for F in (LoginPage, AdminPage, PembeliMenu, KasirPage, PemilikPage, WaiterPage): for F in (LoginPage, AdminPage, PembeliPage, KasirPage, PemilikPage, WaiterPage):
page_name = F.__name__ page_name = F.__name__
frame = F(parent=self.container, controller=self) frame = F(parent=self.container, controller=self)
self.frames[page_name] = frame self.frames[page_name] = frame

View File

@ -1,177 +1,169 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox, simpledialog from tkinter import messagebox, ttk
from PIL import Image, ImageTk
import os
from database import connect from database import connect
class PembeliMenu(tk.Frame): class PembeliPage(tk.Frame): # Ganti nama class jadi PembeliPage
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) super().__init__(parent)
self.controller = controller self.controller = controller
self.keranjang = [] # List untuk simpan belanjaan sementara self.cart = {}
self.image_refs = [] # Supaya gambar tidak hilang 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()
# 1. Frame Kiri (Daftar Menu) # --- UI 1: LOGIN PEMBELI ---
self.left_frame = tk.Frame(self) def show_login_ui(self):
self.left_frame.pack(side="left", fill="both", expand=True) self.menu_frame.pack_forget()
self.login_frame.pack(fill="both", expand=True)
# Header Kiri for w in self.login_frame.winfo_children(): w.destroy()
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)
# Canvas untuk Scroll Menu tk.Label(self.login_frame, text="SELAMAT DATANG", font=("Arial", 20, "bold")).pack(pady=50)
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)
self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) box = tk.Frame(self.login_frame, bd=2, relief="groove", padx=20, pady=20)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") box.pack()
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.pack(side="left", fill="both", expand=True) tk.Label(box, text="Nama Anda:").pack(anchor="w")
self.scrollbar.pack(side="right", fill="y") self.entry_nama = tk.Entry(box, width=30)
self.entry_nama.pack(pady=5)
# 2. Frame Kanan (Keranjang Belanja) tk.Label(box, text="Nomor Meja:").pack(anchor="w")
self.right_frame = tk.Frame(self, bg="#ecf0f1", width=300) self.combo_meja = ttk.Combobox(box, values=[str(i) for i in range(1, 21)], state="readonly")
self.right_frame.pack(side="right", fill="y") self.combo_meja.current(0)
self.right_frame.pack_propagate(False) # Agar lebar tetap 300px self.combo_meja.pack(pady=5)
tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x") 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)
# Listbox Keranjang def validate_login(self):
self.cart_listbox = tk.Listbox(self.right_frame, font=("Arial", 10)) if not self.entry_nama.get():
self.cart_listbox.pack(fill="both", expand=True, padx=10, pady=10) 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()
# Label Total Harga # --- UI 2: MENU MAKANAN ---
self.total_label = tk.Label(self.right_frame, text="Total: Rp 0", font=("Arial", 14, "bold"), bg="#ecf0f1", fg="#e74c3c") def show_menu_ui(self):
self.total_label.pack(pady=10) self.login_frame.pack_forget()
self.menu_frame.pack(fill="both", expand=True)
# Tombol Aksi # Bersihkan frame lama biar gak numpuk
tk.Button(self.right_frame, text="Hapus Item Terpilih", bg="#e67e22", fg="white", command=self.hapus_item).pack(fill="x", padx=10, pady=5) for w in self.menu_frame.winfo_children(): w.destroy()
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)
def update_data(self): # Header
"""Dipanggil saat halaman dibuka""" top = tk.Frame(self.menu_frame, bg="#eee", pady=10)
self.load_menu() top.pack(fill="x")
self.keranjang = [] # Reset keranjang tiap login baru tk.Label(top, text=f"👤 {self.nama_pelanggan} | Meja {self.no_meja}", font=("Arial", 12)).pack(side="left", padx=20)
self.update_keranjang_ui() tk.Button(top, text="Keluar", bg="#ff6b6b", fg="white", command=self.logout).pack(side="right", padx=20)
def load_menu(self): content = tk.Frame(self.menu_frame)
# Bersihkan area menu content.pack(fill="both", expand=True, padx=10, pady=10)
for widget in self.scrollable_frame.winfo_children():
widget.destroy() # Kiri: Daftar Menu
self.image_refs.clear() left_frame = tk.LabelFrame(content, text=" Menu Tersedia ")
left_frame.pack(side="left", fill="both", expand=True)
canvas = tk.Canvas(left_frame)
scrollbar = tk.Scrollbar(left_frame, orient="vertical", command=canvas.yview)
self.scroll_frame = tk.Frame(canvas)
self.scroll_frame.bind("<Configure>", 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() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT * FROM menu") cur.execute("SELECT id, nama, harga, stok FROM menu WHERE stok > 0")
items = cur.fetchall() items = cur.fetchall()
db.close() db.close()
columns = 3 row, col = 0, 0
for index, item in enumerate(items): for item in items:
self.create_card(item, index, columns) 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")
def create_card(self, item, index, columns): tk.Label(card, text=nama, font=("Arial", 10, "bold")).pack()
id_menu, nama, kategori, harga, stok, file_gambar = item tk.Label(card, text=f"Rp {int(harga):,}", fg="green").pack()
row = index // columns tk.Button(card, text="Tambah +", command=lambda i=m_id, n=nama, h=harga: self.add_to_cart(i, n, h)).pack(pady=2)
col = index % columns
card = tk.Frame(self.scrollable_frame, bd=2, relief="groove", bg="white") col += 1
card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") if col > 2:
col = 0
row += 1
# Load Gambar def add_to_cart(self, m_id, nama, harga):
path = os.path.join("aset", file_gambar) if m_id in self.cart:
try: self.cart[m_id]['qty'] += 1
img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS) else:
photo = ImageTk.PhotoImage(img) self.cart[m_id] = {'nama': nama, 'harga': harga, 'qty': 1}
self.image_refs.append(photo) self.refresh_cart()
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() def refresh_cart(self):
tk.Label(card, text=f"Rp {int(harga):,}", fg="green", bg="white").pack() self.cart_list.delete(0, tk.END)
# 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)
total = 0 total = 0
for item in self.keranjang: for m_id, item in self.cart.items():
nama = item[1] sub = item['harga'] * item['qty']
harga = item[3] total += sub
self.cart_listbox.insert(tk.END, f"{nama} - Rp {int(harga):,}") self.cart_list.insert(tk.END, f"{item['qty']}x {item['nama']} = {sub:,}")
total += harga self.lbl_total.config(text=f"Total: Rp {int(total):,}")
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()
def checkout(self): def checkout(self):
if not self.keranjang: if not self.cart: return
messagebox.showwarning("Kosong", "Keranjang masih kosong!")
return
nama_pelanggan = simpledialog.askstring("Input", "Masukkan Nama Pelanggan:") total_val = int(self.lbl_total.cget("text").replace("Total: Rp ","").replace(",",""))
if not nama_pelanggan: return
total_harga = sum(item[3] for item in self.keranjang)
# Simpan ke Database
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
try: # Simpan ke tabel TRANSAKSI (Bukan orders)
# 1. Simpan Transaksi Utama cur.execute("INSERT INTO transaksi (nama_pelanggan, meja_id, total, status) VALUES (?, ?, ?, 'Pending')",
cur.execute(""" (self.nama_pelanggan, self.no_meja, total_val))
INSERT INTO transaksi (nama_pelanggan, total, status) trans_id = cur.lastrowid
VALUES (?, ?, 'Pending')
""", (nama_pelanggan, total_harga))
transaksi_id = cur.lastrowid
# 2. Simpan Detail Transaksi (Looping item di keranjang) # Simpan Detail
for item in self.keranjang: for m_id, item in self.cart.items():
menu_id = item[0] sub = item['harga'] * item['qty']
harga = item[3] cur.execute("INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal) VALUES (?,?,?,?)",
# Masukkan ke detail (trans_id, m_id, item['qty'], sub))
cur.execute(""" # Kurangi Stok
INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal) cur.execute("UPDATE menu SET stok = stok - ? WHERE id=?", (item['qty'], m_id))
VALUES (?, ?, 1, ?)
""", (transaksi_id, menu_id, harga))
# Kurangi Stok Menu db.commit()
cur.execute("UPDATE menu SET stok = stok - 1 WHERE id = ?", (menu_id,)) db.close()
db.commit() messagebox.showinfo("Sukses", "Pesanan terkirim! Mohon tunggu.")
messagebox.showinfo("Berhasil", "Pesanan berhasil dibuat! Silakan bayar di kasir.") self.cart = {}
self.refresh_cart()
self.load_menu_items()
# Reset def logout(self):
self.keranjang = [] self.cart = {}
self.update_keranjang_ui() self.show_login_ui()
self.load_menu() # Reload menu untuk update stok visual
except Exception as e: def update_data(self):
db.rollback() self.show_login_ui()
messagebox.showerror("Error", f"Gagal Checkout: {e}")
finally:
db.close()

View File

@ -1,43 +1,36 @@
import tkinter as tk import tkinter as tk
from database import connect from database import connect
class PemilikPage(tk.Frame): class PemilikPage(tk.Frame): # HARUS inherit tk.Frame
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) super().__init__(parent) # Init parent class
self.controller = controller self.controller = controller
# --- PERBAIKAN: Baris self.pack() sudah DIHAPUS --- tk.Label(self, text="LAPORAN PENJUALAN (OWNER)", font=("Arial", 18, "bold")).pack(pady=20)
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
# Judul Halaman self.box = tk.Frame(self, bd=2, relief="groove", padx=40, pady=40)
tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20) self.box.pack()
# Label Total # Label Total
self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green") self.total_lbl = tk.Label(self.box, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
self.total_lbl.pack(pady=20) self.total_lbl.pack(pady=10)
tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack() self.count_lbl = tk.Label(self.box, text="Jumlah Transaksi: 0", font=("Arial", 12))
self.count_lbl.pack(pady=5)
# Tombol-tombol tk.Button(self, text="Refresh Laporan", bg="#cfe2ff", command=self.update_data).pack(pady=10)
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=5)
tk.Button(self, text="Logout", bg="#f9e79f", command=lambda: controller.show_frame("LoginPage")).pack(pady=10)
def update_data(self): def update_data(self):
"""Dipanggil otomatis saat halaman dibuka"""
self.load()
def load(self):
"""Ambil data total penjualan dari database"""
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Hitung total dari transaksi yang statusnya 'Paid'
# Hitung sum total dari transaksi yang sudah Paid (Lunas) cur.execute("SELECT SUM(total), COUNT(id) FROM transaksi WHERE status='Paid'")
cur.execute("SELECT SUM(total) FROM transaksi WHERE status='Paid'") res = cur.fetchone()
result = cur.fetchone()[0]
# Kalau belum ada penjualan, set 0
total = result if result else 0
db.close() db.close()
# Update tampilan total_duit = res[0] if res[0] else 0
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}") 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}")

View File

@ -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.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) tk.Button(top, text="Logout", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10)
# List Pesanan # Content
tk.Label(self, text="Pesanan Status: PENDING (Perlu Diantar)", font=("Arial", 12, "bold")).pack(pady=10) 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) self.list_orders.pack(pady=5)
btn_frame = tk.Frame(self) btn_frame = tk.Frame(self)
btn_frame.pack(pady=10) 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) tk.Button(btn_frame, text="✅ Selesai Diantar (Served)", bg="#81C784", command=self.mark_served).pack(side="left", padx=5)
def update_data(self): def update_data(self):
@ -31,14 +31,14 @@ class WaiterPage(tk.Frame):
db = connect() db = connect()
cur = db.cursor() 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'") cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Pending'")
self.data_orders = cur.fetchall() self.data_orders = cur.fetchall()
db.close() db.close()
for item in self.data_orders: for item in self.data_orders:
# item: (id, nama, meja, total) # 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])}") 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): def mark_served(self):
idx = self.list_orders.curselection() idx = self.list_orders.curselection()
@ -46,15 +46,14 @@ class WaiterPage(tk.Frame):
messagebox.showwarning("Pilih", "Pilih pesanan dulu!") messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
return return
selected = self.data_orders[idx[0]] trans_id = self.data_orders[idx[0]][0]
transaksi_id = selected[0]
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Update Status Transaksi jadi Served # Update status jadi 'Served' biar muncul di Kasir
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (transaksi_id,)) cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (trans_id,))
db.commit() db.commit()
db.close() db.close()
messagebox.showinfo("Sukses", "Pesanan Meja selesai dilayani!") messagebox.showinfo("Sukses", "Pesanan Meja ini sudah dilayani!")
self.update_data() self.update_data()