Compare commits

..

No commits in common. "2bc30165e2d8654de91438542c7dc55ab4404d85" and "1bb52206cc86eff84b49514662e3d22255045f54" have entirely different histories.

16 changed files with 275 additions and 279 deletions

View File

@ -4,21 +4,27 @@ from database import connect
import os import os
import shutil import shutil
class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage # 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: self.controller.show_frame("LoginPage")).pack(side="right", padx=10) tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10)
# --- Form Input (Kiri) --- # --- Layout Kiri (Form) & Kanan (Tabel) ---
left_frame = tk.Frame(self, padx=10, pady=10) main_content = tk.Frame(self)
left_frame.pack(side="left", fill="y") 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)
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)
@ -30,6 +36,7 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
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")
@ -38,17 +45,16 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
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)
# --- Tabel Menu (Kanan) --- # --- KANAN ---
right_frame = tk.Frame(self, padx=10, pady=10) right_frame = tk.Frame(main_content)
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 Database:", font=("Arial", 10, "bold")).pack(anchor="w") tk.Label(right_frame, text="Daftar Menu:", 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)
@ -73,7 +79,7 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
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=[("Image Files", "*.png;*.jpg;*.jpeg")]) filename = filedialog.askopenfilename(title="Pilih Gambar", filetypes=[("Images", "*.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")
@ -90,21 +96,20 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
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")
nama_file_asli = os.path.basename(self.selected_image_path) destinasi = os.path.join("img", os.path.basename(self.selected_image_path))
destinasi = os.path.join("img", nama_file_asli) try:
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 if stok else 0, final_image_path)) (nama, harga, stok, 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))
@ -113,14 +118,12 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
def delete_menu(self): def delete_menu(self):
idx = self.list_menu.curselection() idx = self.list_menu.curselection()
if not idx: if not idx: return
messagebox.showwarning("Pilih", "Pilih menu dulu!")
return
id_menu = self.data_menu[idx[0]][0] selected = self.data_menu[idx[0]]
if messagebox.askyesno("Hapus", "Yakin hapus menu ini?"): if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"):
db = connect() db = connect()
db.cursor().execute("DELETE FROM menu WHERE id=?", (id_menu,)) db.cursor().execute("DELETE FROM menu WHERE id=?", (selected[0],))
db.commit() db.commit()
db.close() db.close()
self.update_data() self.update_data()

View File

@ -1,3 +1,4 @@
# File: database.py
import sqlite3 import sqlite3
def connect(): def connect():
@ -29,7 +30,7 @@ def setup_database():
) )
""") """)
# 3. Transaksi (Header) # 3. Transaksi
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,
@ -41,7 +42,6 @@ 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 Data User Dummy # Seeding User (Kalau kosong diisi default)
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 bos!") print("Database Ready!")
if __name__ == "__main__": if __name__ == "__main__":
setup_database() setup_database()

View File

@ -1,49 +1,32 @@
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():
# 1. Pastikan database dan tabel sudah dibuat conn = sqlite3.connect("cafe.db")
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()
print("♻️ Membersihkan data lama...") # Hapus data lama biar gak dobel (opsional, biar bersih)
cur.execute("DELETE FROM menu") cur.execute("DELETE FROM menu")
cur.execute("DELETE FROM sqlite_sequence WHERE name='menu'") # Reset ID jadi 1
# 3. Data Menu (Path gambar pakai 'aset/') # Data Menu (Sesuaikan nama file gambar dengan yang ada di folder aset kamu)
menus = [ menus = [
("Ayam Goreng", "Makanan", 15000, 20, "aset/ayam_goreng.jpg"), # (Nama, Kategori, Harga, Stok, Nama File Gambar)
("Bakso Urat", "Makanan", 12000, 15, "aset/bakso.jpg"), ("Ayam Goreng", "Makanan", 15000, 20, "ayam_goreng.jpg"),
("Mie Ayam", "Makanan", 10000, 25, "aset/mie_ayam.jpg"), ("Bakso Urat", "Makanan", 12000, 15, "bakso.jpg"),
("Es Teh Manis", "Minuman", 3000, 50, "aset/es_teh.jpg"), ("Mie Ayam", "Makanan", 10000, 25, "mie_ayam.jpg"),
("Jus Jeruk", "Minuman", 5000, 30, "aset/jus_jeruk.jpg"), ("Es Teh Manis", "Minuman", 3000, 50, "es_teh.jpg"),
("Jus Jeruk", "Minuman", 5000, 30, "jus_jeruk.jpg"),
] ]
print("📥 Sedang mengisi data menu...") print("Sedang mengisi data menu...")
try: cur.executemany("""
cur.executemany(""" INSERT INTO menu (nama, kategori, harga, stok, gambar)
INSERT INTO menu (nama, kategori, harga, stok, gambar) VALUES (?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?) """, menus)
""", menus)
conn.commit() conn.commit()
print("✅ MANTAP! Data menu berhasil masuk. Sekarang jalankan main.py!") conn.close()
except Exception as e: print("✅ Berhasil! Data menu sudah masuk database.")
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 # List Order
tk.Label(self, text="Tagihan Belum Bayar (Status: Served):", font=("Arial", 11)).pack(pady=10) 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) self.order_list = tk.Listbox(self, width=80, height=15, font=("Arial", 10))
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=20, pady=20) tk.Button(self, text="💰 PROSES BAYAR (LUNAS)", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=50, 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,36 +27,35 @@ class KasirPage(tk.Frame):
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Kasir melihat yang sudah diantar (Served) tapi belum bayar # Kasir melihat yang statusnya Served
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 Transaksi: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3]):,}") self.order_list.insert(tk.END, f"ID: {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 dulu!") messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!")
return return
selected = self.data_orders[idx[0]] selected = self.data_orders[idx[0]]
trans_id = selected[0] transaksi_id = selected[0]
nama = selected[1]
total = selected[3] total = selected[3]
# Konfirmasi confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?")
if not messagebox.askyesno("Konfirmasi", f"Terima pembayaran sebesar Rp {int(total):,}?"): if confirm:
return db = connect()
cur = db.cursor()
db = connect() # Update status jadi 'Paid' dan metode pembayaran (misal Cash default)
cur = db.cursor() cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='Cash' WHERE id=?", (transaksi_id,))
# Update status jadi 'Paid' db.commit()
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='CASH' WHERE id=?", (trans_id,)) db.close()
db.commit() messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
db.close() self.update_data()
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
self.update_data()

View File

@ -2,57 +2,52 @@ 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
tk.Label(self, text="LOGIN SYSTEM", font=("Arial", 20, "bold")).pack(pady=40) # Center Content
center_frame = tk.Frame(self)
center_frame.place(relx=0.5, rely=0.5, anchor="center")
frame_login = tk.Frame(self) tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20)
frame_login.pack()
tk.Label(frame_login, text="Username:").pack() tk.Label(center_frame, text="Username").pack(anchor="w")
self.user = tk.Entry(frame_login) self.user = tk.Entry(center_frame, width=30)
self.user.pack(pady=5) self.user.pack(pady=5)
tk.Label(frame_login, text="Password:").pack() tk.Label(center_frame, text="Password").pack(anchor="w")
self.passw = tk.Entry(frame_login, show="*") self.passw = tk.Entry(center_frame, show="*", width=30)
self.passw.pack(pady=5) self.passw.pack(pady=5)
tk.Button(frame_login, text="LOGIN", command=self.login, width=20, bg="#2196F3", fg="white").pack(pady=20) tk.Button(center_frame, text="Login", bg="#4CAF50", fg="white", width=20, command=self.login).pack(pady=20)
def login(self): def login(self):
u = self.user.get() username = self.user.get()
p = self.passw.get() password = self.passw.get()
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (u, p)) cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username, password))
res = cur.fetchone() res = cur.fetchone()
db.close() db.close()
if not res: if not res:
messagebox.showerror("Gagal", "Username/Password salah!") messagebox.showerror("Error", "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)
# Redirect sesuai Role # Arahkan sesuai Role (Nama Class harus sama dengan di main.py)
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("PembeliPage") self.controller.show_frame("PembeliMenu")
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
# Import class yang sudah kita benerin namanya # Pastikan nama file dan nama class sesuai
from login import LoginPage from login import LoginPage
from admin_menu import AdminPage from admin_menu import AdminPage # Class diganti jadi AdminPage di file admin_menu
from pembeli_menu import PembeliPage from pembeli_menu import PembeliMenu # Class tetap PembeliMenu
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 Python") self.title("Sistem Manajemen Kafe")
self.geometry("1000x700") self.geometry("1000x700")
setup_database() # Auto bikin tabel setup_database()
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 halaman # Loop semua class halaman
for F in (LoginPage, AdminPage, PembeliPage, KasirPage, PemilikPage, WaiterPage): for F in (LoginPage, AdminPage, PembeliMenu, 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,169 +1,177 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox, ttk from tkinter import messagebox, simpledialog
from PIL import Image, ImageTk
import os
from database import connect from database import connect
class PembeliPage(tk.Frame): # Ganti nama class jadi PembeliPage class PembeliMenu(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.cart = {} self.keranjang = [] # List untuk simpan belanjaan sementara
self.nama_pelanggan = "" self.image_refs = [] # Supaya gambar tidak hilang
self.no_meja = ""
self.login_frame = tk.Frame(self) # --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) ---
self.menu_frame = tk.Frame(self)
self.show_login_ui()
# --- UI 1: LOGIN PEMBELI --- # 1. Frame Kiri (Daftar Menu)
def show_login_ui(self): self.left_frame = tk.Frame(self)
self.menu_frame.pack_forget() self.left_frame.pack(side="left", fill="both", expand=True)
self.login_frame.pack(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) # 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)
box = tk.Frame(self.login_frame, bd=2, relief="groove", padx=20, pady=20) self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
box.pack() self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
tk.Label(box, text="Nama Anda:").pack(anchor="w") self.canvas.pack(side="left", fill="both", expand=True)
self.entry_nama = tk.Entry(box, width=30) self.scrollbar.pack(side="right", fill="y")
self.entry_nama.pack(pady=5)
tk.Label(box, text="Nomor Meja:").pack(anchor="w") # 2. Frame Kanan (Keranjang Belanja)
self.combo_meja = ttk.Combobox(box, values=[str(i) for i in range(1, 21)], state="readonly") self.right_frame = tk.Frame(self, bg="#ecf0f1", width=300)
self.combo_meja.current(0) self.right_frame.pack(side="right", fill="y")
self.combo_meja.pack(pady=5) self.right_frame.pack_propagate(False) # Agar lebar tetap 300px
tk.Button(box, text="MULAI PESAN", bg="#4CAF50", fg="white", command=self.validate_login).pack(pady=20, fill="x") tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x")
tk.Button(self.login_frame, text="Kembali", command=lambda: self.controller.show_frame("LoginPage")).pack(pady=10)
def validate_login(self): # Listbox Keranjang
if not self.entry_nama.get(): self.cart_listbox = tk.Listbox(self.right_frame, font=("Arial", 10))
messagebox.showwarning("Oops", "Nama wajib diisi!") self.cart_listbox.pack(fill="both", expand=True, padx=10, pady=10)
return
self.nama_pelanggan = self.entry_nama.get()
self.no_meja = self.combo_meja.get()
self.show_menu_ui()
# --- UI 2: MENU MAKANAN --- # Label Total Harga
def show_menu_ui(self): self.total_label = tk.Label(self.right_frame, text="Total: Rp 0", font=("Arial", 14, "bold"), bg="#ecf0f1", fg="#e74c3c")
self.login_frame.pack_forget() self.total_label.pack(pady=10)
self.menu_frame.pack(fill="both", expand=True)
# Bersihkan frame lama biar gak numpuk # Tombol Aksi
for w in self.menu_frame.winfo_children(): w.destroy() 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)
# Header def update_data(self):
top = tk.Frame(self.menu_frame, bg="#eee", pady=10) """Dipanggil saat halaman dibuka"""
top.pack(fill="x") self.load_menu()
tk.Label(top, text=f"👤 {self.nama_pelanggan} | Meja {self.no_meja}", font=("Arial", 12)).pack(side="left", padx=20) self.keranjang = [] # Reset keranjang tiap login baru
tk.Button(top, text="Keluar", bg="#ff6b6b", fg="white", command=self.logout).pack(side="right", padx=20) self.update_keranjang_ui()
content = tk.Frame(self.menu_frame) def load_menu(self):
content.pack(fill="both", expand=True, padx=10, pady=10) # Bersihkan area menu
for widget in self.scrollable_frame.winfo_children():
# Kiri: Daftar Menu widget.destroy()
left_frame = tk.LabelFrame(content, text=" Menu Tersedia ") self.image_refs.clear()
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 id, nama, harga, stok FROM menu WHERE stok > 0") cur.execute("SELECT * FROM menu")
items = cur.fetchall() items = cur.fetchall()
db.close() db.close()
row, col = 0, 0 columns = 3
for item in items: for index, item in enumerate(items):
m_id, nama, harga, stok = item self.create_card(item, index, columns)
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() def create_card(self, item, index, columns):
tk.Label(card, text=f"Rp {int(harga):,}", fg="green").pack() id_menu, nama, kategori, harga, stok, file_gambar = item
tk.Button(card, text="Tambah +", command=lambda i=m_id, n=nama, h=harga: self.add_to_cart(i, n, h)).pack(pady=2) row = index // columns
col = index % columns
col += 1 card = tk.Frame(self.scrollable_frame, bd=2, relief="groove", bg="white")
if col > 2: card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
col = 0
row += 1
def add_to_cart(self, m_id, nama, harga): # Load Gambar
if m_id in self.cart: path = os.path.join("aset", file_gambar)
self.cart[m_id]['qty'] += 1 try:
else: img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS)
self.cart[m_id] = {'nama': nama, 'harga': harga, 'qty': 1} photo = ImageTk.PhotoImage(img)
self.refresh_cart() 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)
def refresh_cart(self): tk.Label(card, text=nama, font=("Arial", 10, "bold"), bg="white").pack()
self.cart_list.delete(0, tk.END) 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)
total = 0 total = 0
for m_id, item in self.cart.items(): for item in self.keranjang:
sub = item['harga'] * item['qty'] nama = item[1]
total += sub harga = item[3]
self.cart_list.insert(tk.END, f"{item['qty']}x {item['nama']} = {sub:,}") self.cart_listbox.insert(tk.END, f"{nama} - Rp {int(harga):,}")
self.lbl_total.config(text=f"Total: Rp {int(total):,}") 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()
def checkout(self): def checkout(self):
if not self.cart: return if not self.keranjang:
messagebox.showwarning("Kosong", "Keranjang masih kosong!")
return
total_val = int(self.lbl_total.cget("text").replace("Total: Rp ","").replace(",","")) nama_pelanggan = simpledialog.askstring("Input", "Masukkan Nama Pelanggan:")
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()
# Simpan ke tabel TRANSAKSI (Bukan orders) try:
cur.execute("INSERT INTO transaksi (nama_pelanggan, meja_id, total, status) VALUES (?, ?, ?, 'Pending')", # 1. Simpan Transaksi Utama
(self.nama_pelanggan, self.no_meja, total_val)) cur.execute("""
trans_id = cur.lastrowid INSERT INTO transaksi (nama_pelanggan, total, status)
VALUES (?, ?, 'Pending')
""", (nama_pelanggan, total_harga))
transaksi_id = cur.lastrowid
# Simpan Detail # 2. Simpan Detail Transaksi (Looping item di keranjang)
for m_id, item in self.cart.items(): for item in self.keranjang:
sub = item['harga'] * item['qty'] menu_id = item[0]
cur.execute("INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal) VALUES (?,?,?,?)", harga = item[3]
(trans_id, m_id, item['qty'], sub)) # Masukkan ke detail
# Kurangi Stok cur.execute("""
cur.execute("UPDATE menu SET stok = stok - ? WHERE id=?", (item['qty'], m_id)) INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal)
VALUES (?, ?, 1, ?)
""", (transaksi_id, menu_id, harga))
db.commit() # Kurangi Stok Menu
db.close() cur.execute("UPDATE menu SET stok = stok - 1 WHERE id = ?", (menu_id,))
messagebox.showinfo("Sukses", "Pesanan terkirim! Mohon tunggu.") db.commit()
self.cart = {} messagebox.showinfo("Berhasil", "Pesanan berhasil dibuat! Silakan bayar di kasir.")
self.refresh_cart()
self.load_menu_items()
def logout(self): # Reset
self.cart = {} self.keranjang = []
self.show_login_ui() self.update_keranjang_ui()
self.load_menu() # Reload menu untuk update stok visual
def update_data(self): except Exception as e:
self.show_login_ui() db.rollback()
messagebox.showerror("Error", f"Gagal Checkout: {e}")
finally:
db.close()

View File

@ -1,36 +1,43 @@
import tkinter as tk import tkinter as tk
from database import connect from database import connect
class PemilikPage(tk.Frame): # HARUS inherit tk.Frame class PemilikPage(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) # Init parent class super().__init__(parent)
self.controller = controller self.controller = controller
tk.Label(self, text="LAPORAN PENJUALAN (OWNER)", font=("Arial", 18, "bold")).pack(pady=20) # --- PERBAIKAN: Baris self.pack() sudah DIHAPUS ---
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
self.box = tk.Frame(self, bd=2, relief="groove", padx=40, pady=40) # Judul Halaman
self.box.pack() tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20)
# Label Total # Label Total
self.total_lbl = tk.Label(self.box, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green") self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
self.total_lbl.pack(pady=10) self.total_lbl.pack(pady=20)
self.count_lbl = tk.Label(self.box, text="Jumlah Transaksi: 0", font=("Arial", 12)) tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack()
self.count_lbl.pack(pady=5)
tk.Button(self, text="Refresh Laporan", bg="#cfe2ff", command=self.update_data).pack(pady=10) # Tombol-tombol
tk.Button(self, text="Logout", bg="#f9e79f", command=lambda: controller.show_frame("LoginPage")).pack(pady=5) 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)
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'
cur.execute("SELECT SUM(total), COUNT(id) FROM transaksi WHERE status='Paid'") # Hitung sum total dari transaksi yang sudah Paid (Lunas)
res = cur.fetchone() 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
db.close() db.close()
total_duit = res[0] if res[0] else 0 # Update tampilan
total_transaksi = res[1] if res[1] else 0 self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}")
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)
# Content # List Pesanan
tk.Label(self, text="Pesanan Pending (Perlu Diantar):", font=("Arial", 12)).pack(pady=10) tk.Label(self, text="Pesanan Status: PENDING (Perlu Diantar)", font=("Arial", 12, "bold")).pack(pady=10)
self.list_orders = tk.Listbox(self, width=80, height=15) self.list_orders = tk.Listbox(self, width=80, height=15, font=("Arial", 10))
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", command=self.update_data).pack(side="left", padx=5) tk.Button(btn_frame, text="Refresh Data", 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: {item[0]} | Meja: {item[2]} | A.N: {item[1]} | Total: Rp {int(item[3]):,}") self.list_orders.insert(tk.END, f"ID Transaksi: {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,14 +46,15 @@ class WaiterPage(tk.Frame):
messagebox.showwarning("Pilih", "Pilih pesanan dulu!") messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
return return
trans_id = self.data_orders[idx[0]][0] selected = self.data_orders[idx[0]]
transaksi_id = selected[0]
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Update status jadi 'Served' biar muncul di Kasir # Update Status Transaksi jadi Served
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (trans_id,)) cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (transaksi_id,))
db.commit() db.commit()
db.close() db.close()
messagebox.showinfo("Sukses", "Pesanan Meja ini sudah dilayani!") messagebox.showinfo("Sukses", "Pesanan Meja selesai dilayani!")
self.update_data() self.update_data()