Compare commits

..

No commits in common. "1bb52206cc86eff84b49514662e3d22255045f54" and "5400541b34bda0fa477cec556aae41de55ea7fd7" have entirely different histories.

22 changed files with 274 additions and 519 deletions

View File

@ -1,129 +1,40 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox, filedialog
from database import connect from database import connect
import os from tkinter import messagebox
import shutil
# GANTI NAMA CLASS JADI AdminPage class AdminMenu:
class AdminPage(tk.Frame): def __init__(self, root):
def __init__(self, parent, controller): self.root = root
super().__init__(parent) self.frame = tk.Frame(root)
self.controller = controller self.frame.pack()
self.selected_image_path = None
self.data_menu = []
# --- Header --- tk.Label(
top = tk.Frame(self, bg="#333") self.frame,
top.pack(fill="x") text="ADMIN - KELOLA MENU",
tk.Label(top, text="ADMIN DASHBOARD", font=("Arial", 16, "bold"), fg="white", bg="#333").pack(side="left", padx=10, pady=10) font=("Arial", 18, "bold")
tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10) ).pack(pady=10)
# --- Layout Kiri (Form) & Kanan (Tabel) --- self.nama = tk.Entry(self.frame)
main_content = tk.Frame(self) self.harga = tk.Entry(self.frame)
main_content.pack(fill="both", expand=True, padx=10, pady=10) self.gambar = tk.Entry(self.frame)
# --- KIRI --- for label, entry in [
left_frame = tk.Frame(main_content) ("Nama Menu", self.nama),
left_frame.pack(side="left", fill="y", padx=10) ("Harga", self.harga),
("Path Gambar", self.gambar)
]:
tk.Label(self.frame, text=label).pack()
entry.pack()
tk.Label(left_frame, text="Nama Menu:").pack(anchor="w") tk.Button(self.frame, text="Tambah Menu", command=self.tambah).pack(pady=5)
self.entry_nama = tk.Entry(left_frame, width=30)
self.entry_nama.pack(pady=5)
tk.Label(left_frame, text="Harga:").pack(anchor="w")
self.entry_harga = tk.Entry(left_frame, width=30)
self.entry_harga.pack(pady=5)
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")
self.btn_img = tk.Button(left_frame, text="Pilih Gambar", command=self.browse_image)
self.btn_img.pack(pady=5, anchor="w")
self.lbl_img_path = tk.Label(left_frame, text="Belum ada gambar", fg="gray", font=("Arial", 8))
self.lbl_img_path.pack(anchor="w")
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)
right_frame.pack(side="right", fill="both", expand=True)
tk.Label(right_frame, text="Daftar Menu:", font=("Arial", 10, "bold")).pack(anchor="w")
self.list_menu = tk.Listbox(right_frame)
self.list_menu.pack(fill="both", expand=True)
scrollbar = tk.Scrollbar(self.list_menu)
scrollbar.pack(side="right", fill="y")
self.list_menu.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.list_menu.yview)
def update_data(self):
self.list_menu.delete(0, tk.END)
self.data_menu = []
def tambah(self):
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT id, nama, harga, stok FROM menu") cur.execute(
rows = cur.fetchall() "INSERT INTO menu(nama,harga,gambar) VALUES (?,?,?)",
db.close() (self.nama.get(), self.harga.get(), self.gambar.get())
)
for row in rows:
self.data_menu.append(row)
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")])
if filename:
self.selected_image_path = filename
self.lbl_img_path.config(text=os.path.basename(filename), fg="black")
def add_menu(self):
nama = self.entry_nama.get()
harga = self.entry_harga.get()
stok = self.entry_stok.get()
if not nama or not harga:
messagebox.showwarning("Warning", "Nama dan Harga wajib diisi!")
return
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
db = connect()
cur = db.cursor()
try:
cur.execute("INSERT INTO menu (nama, harga, stok, gambar) VALUES (?, ?, ?, ?)",
(nama, harga, stok, final_image_path))
db.commit()
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan!")
self.update_data()
except Exception as e:
messagebox.showerror("Error", str(e))
finally:
db.close()
def delete_menu(self):
idx = self.list_menu.curselection()
if not idx: return
selected = self.data_menu[idx[0]]
if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"):
db = connect()
db.cursor().execute("DELETE FROM menu WHERE id=?", (selected[0],))
db.commit() db.commit()
db.close() db.close()
self.update_data() messagebox.showinfo("Sukses", "Menu berhasil ditambahkan")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

View File

@ -1,4 +1,3 @@
# File: database.py
import sqlite3 import sqlite3
def connect(): def connect():
@ -8,7 +7,7 @@ def setup_database():
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# 1. Users # Users
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS users( CREATE TABLE IF NOT EXISTS users(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -18,57 +17,56 @@ def setup_database():
) )
""") """)
# 2. Menu # Menu
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS menu( CREATE TABLE IF NOT EXISTS menu(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
nama TEXT, nama TEXT,
kategori TEXT,
harga REAL, harga REAL,
stok INTEGER,
gambar TEXT gambar TEXT
) )
""") """)
# 3. Transaksi # Orders
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS transaksi( CREATE TABLE IF NOT EXISTS orders(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
nama_pelanggan TEXT, nama TEXT,
meja_id INTEGER, total REAL
tanggal TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total REAL,
metode_pembayaran TEXT,
status TEXT DEFAULT 'Pending'
) )
""") """)
# 4. Detail Transaksi # Pembayaran
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS detail_transaksi( CREATE TABLE IF NOT EXISTS pembayaran(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
transaksi_id INTEGER, order_id INTEGER,
menu_id INTEGER, total REAL
jumlah INTEGER,
subtotal REAL
) )
""") """)
# Seeding User (Kalau kosong diisi default) # User 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 = [
("admin","admin","admin"), ("admin","admin","admin"),
("kasir","kasir","kasir"), ("kasir","kasir","kasir"),
("pembeli","pembeli","pembeli"), ("pembeli","pembeli","pembeli"),
("pemilik","pemilik","pemilik"), ("pemilik","pemilik","pemilik")
("waiter","waiter","waiter")
] ]
cur.executemany("INSERT INTO users(username,password,role) VALUES (?,?,?)", users) cur.executemany("INSERT INTO users(username,password,role) VALUES (?,?,?)", users)
db.commit() db.commit()
db.close()
print("Database Ready!")
if __name__ == "__main__": # Menu default
setup_database() cur.execute("SELECT COUNT(*) FROM menu")
if cur.fetchone()[0] == 0:
menu = [
("Mie Ayam", 18000, "mie ayam.webp"),
("Mie Kuah", 10000, "mie kuah.webp"),
("Es Teh Manis", 5000, "es teh.webp"),
("Jus Jeruk", 8000, "jus jeruk.webp")
]
cur.executemany("INSERT INTO menu(nama,harga,gambar) VALUES (?,?,?)", menu)
db.commit()
db.close()

View File

@ -1,32 +0,0 @@
import sqlite3
def isi_data_awal():
conn = sqlite3.connect("cafe.db")
cur = conn.cursor()
# Hapus data lama biar gak dobel (opsional, biar bersih)
cur.execute("DELETE FROM menu")
# Data Menu (Sesuaikan nama file gambar dengan yang ada di folder aset kamu)
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"),
]
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.")
if __name__ == "__main__":
isi_data_awal()

View File

@ -2,60 +2,73 @@ import tkinter as tk
from tkinter import messagebox from tkinter import messagebox
from database import connect from database import connect
class KasirPage(tk.Frame): class KasirPage:
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) self.parent = parent
self.controller = controller self.controller = controller
self.frame = tk.Frame(parent)
self.frame.pack(fill="both", expand=True)
# Header tk.Label(self.frame, text="KASIR PAGE", font=("Arial", 18, "bold")).pack(pady=10)
top = tk.Frame(self, bg="#ddd")
top.pack(fill="x")
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 # Tombol Logout
tk.Label(self, text="Tagihan Belum Lunas (Status: Served):", font=("Arial", 11)).pack(pady=10) tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
self.order_list = tk.Listbox(self, width=80, height=15, font=("Arial", 10))
self.order_list.pack(pady=5)
tk.Button(self, text="Refresh Data", command=self.update_data).pack(pady=5) # Listbox untuk menampilkan order
tk.Button(self, text="💰 PROSES BAYAR (LUNAS)", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=50, pady=20) tk.Label(self.frame, text="Daftar Order:", font=("Arial", 12, "bold")).pack(pady=5)
self.listbox = tk.Listbox(self.frame, width=50, height=10)
self.listbox.pack(pady=5)
def update_data(self): # Tombol bayar
self.order_list.delete(0, tk.END) tk.Button(self.frame, text="Bayar", bg="#d1e7dd", command=self.bayar).pack(pady=5)
self.data_orders = []
self.load_orders() # Load order dari database saat awal
def load_orders(self):
self.listbox.delete(0, tk.END)
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Kasir melihat yang statusnya Served cur.execute("SELECT id, nama, harga FROM orders")
cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Served'") self.data = cur.fetchall()
self.data_orders = cur.fetchall()
db.close() db.close()
for item in self.data_orders: for order in self.data:
self.order_list.insert(tk.END, f"ID: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3])}") self.listbox.insert(tk.END, f"ID {order[0]}: {order[1]} - Rp {order[2]:,}")
def bayar(self): def bayar(self):
idx = self.order_list.curselection() idx = self.listbox.curselection()
if not idx: if not idx:
messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!") messagebox.showwarning("Pilih Order", "Pilih order yang ingin dibayar!")
return return
selected = self.data_orders[idx[0]] order = self.data[idx[0]]
transaksi_id = selected[0] order_id, nama, total = order
nama = selected[1]
total = selected[3]
confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?") # Simpan pembayaran di database (opsional, bisa buat tabel pembayaran)
if confirm:
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("INSERT INTO pembayaran VALUES (NULL, ?, ?)", (order_id, total))
# Update status jadi 'Paid' dan metode pembayaran (misal Cash default) cur.execute("DELETE FROM orders WHERE id=?", (order_id,))
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='Cash' WHERE id=?", (transaksi_id,))
db.commit() db.commit()
db.close() db.close()
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!") # Tampilkan struk
self.update_data() self.tampil_struk(order_id, nama, total)
# Update listbox
self.load_orders()
def tampil_struk(self, order_id, nama, total):
win = tk.Toplevel(self.frame)
win.title("Struk Pembayaran")
tk.Label(win, text="STRUK PEMBAYARAN", font=("Arial",14,"bold")).pack(pady=5)
tk.Label(win, text=f"Order ID : {order_id}").pack()
tk.Label(win, text=f"Nama Menu: {nama}").pack()
tk.Label(win, text=f"Total : Rp {total:,}").pack()
tk.Label(win, text="Terima kasih 🙏").pack(pady=10)
tk.Button(win, text="Tutup", command=win.destroy).pack(pady=5)
def logout(self):
self.frame.destroy()
self.controller.show_frame("Login")

View File

@ -1,56 +1,46 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox from tkinter import messagebox
from database import connect from database import connect
from admin_menu import AdminMenu
from pembeli_menu import PembeliMenu
from kasir import KasirPage
from pemilik import PemilikPage
class LoginPage(tk.Frame): class LoginPage:
def __init__(self, parent, controller): def __init__(self, root):
super().__init__(parent) self.root = root
self.controller = controller self.frame = tk.Frame(root)
self.frame.pack(expand=True)
# Center Content tk.Label(self.frame, text="LOGIN CAFE", font=("Arial",20,"bold")).pack(pady=10)
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) self.user = tk.Entry(self.frame)
self.passw = tk.Entry(self.frame, show="*")
self.user.pack()
self.passw.pack()
tk.Label(center_frame, text="Username").pack(anchor="w") tk.Button(self.frame, text="Login", command=self.login).pack(pady=5)
self.user = tk.Entry(center_frame, width=30)
self.user.pack(pady=5)
tk.Label(center_frame, text="Password").pack(anchor="w")
self.passw = tk.Entry(center_frame, show="*", width=30)
self.passw.pack(pady=5)
tk.Button(center_frame, text="Login", bg="#4CAF50", fg="white", width=20, command=self.login).pack(pady=20)
def login(self): def login(self):
username = self.user.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=?", (username, password)) cur.execute("SELECT role FROM users WHERE username=? AND password=?",
(self.user.get(), self.passw.get()))
res = cur.fetchone() res = cur.fetchone()
db.close() db.close()
if not res: if not res:
messagebox.showerror("Error", "Username/Password salah!") messagebox.showerror("Error","Login gagal")
return return
self.frame.destroy()
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)
if role=="admin": if role=="admin":
self.controller.show_frame("AdminPage") AdminMenu(self.root)
elif role=="pembeli": elif role=="pembeli":
self.controller.show_frame("PembeliMenu") PembeliMenu(self.root)
elif role=="kasir": elif role=="kasir":
self.controller.show_frame("KasirPage") KasirPage(self.root)
elif role=="pemilik": elif role=="pemilik":
self.controller.show_frame("PemilikPage") PemilikPage(self.root)
elif role == "waiter":
self.controller.show_frame("WaiterPage")

View File

@ -1,44 +1,60 @@
import tkinter as tk import tkinter as tk
from database import setup_database from database import setup_database, connect
from pembeli_menu import PembeliMenu
# Pastikan nama file dan nama class sesuai
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 kasir import KasirPage from kasir import KasirPage
from pemilik import PemilikPage from pemilik import PemilikPage
from waiter_dashboard import WaiterPage from tkinter import messagebox
class CafeApp(tk.Tk): # --- Login Screen ---
class LoginScreen:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(root)
self.frame.pack(fill="both", expand=True)
tk.Label(self.frame,text="LOGIN SISTEM CAFE", font=("Arial",18,"bold")).pack(pady=20)
tk.Label(self.frame,text="Username").pack()
self.username_entry = tk.Entry(self.frame)
self.username_entry.pack()
tk.Label(self.frame,text="Password").pack()
self.password_entry = tk.Entry(self.frame, show="*")
self.password_entry.pack(pady=5)
tk.Button(self.frame,text="Login", bg="#cfe2ff", command=self.login).pack(pady=10)
def login(self):
username = self.username_entry.get()
password = self.password_entry.get()
db = connect()
cur = db.cursor()
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username,password))
result = cur.fetchone()
db.close()
if result:
role = result[0]
self.frame.destroy()
if role=="pembeli":
PembeliMenu(self.root)
elif role=="kasir":
KasirPage(self.root)
elif role=="pemilik":
PemilikPage(self.root)
else:
messagebox.showerror("Error","Role tidak dikenali")
else:
messagebox.showerror("Error","Username / Password salah")
# --- App ---
class App(tk.Tk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("Sistem Manajemen Kafe") self.title("Sistem Cafe")
self.geometry("1000x700") self.geometry("700x600")
setup_database()
self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True)
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.frames = {}
# Loop semua class halaman
for F in (LoginPage, AdminPage, PembeliMenu, KasirPage, PemilikPage, WaiterPage):
page_name = F.__name__
frame = F(parent=self.container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("LoginPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
if hasattr(frame, "update_data"):
frame.update_data()
frame.tkraise()
# --- Run Program ---
if __name__=="__main__": if __name__=="__main__":
app = CafeApp() setup_database()
app = App()
LoginScreen(app)
app.mainloop() app.mainloop()

View File

@ -1,177 +1,91 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox, simpledialog
from PIL import Image, ImageTk from PIL import Image, ImageTk
import os
from database import connect from database import connect
from tkinter import messagebox
class PembeliMenu(tk.Frame): class PembeliMenu:
def __init__(self, parent, controller): def __init__(self, parent):
super().__init__(parent) self.parent = parent
self.controller = controller self.frame = tk.Frame(parent)
self.keranjang = [] # List untuk simpan belanjaan sementara self.frame.pack(fill="both", expand=True)
self.image_refs = [] # Supaya gambar tidak hilang
# --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) ---
# 1. Frame Kiri (Daftar Menu) self.cart = [] # Simpan tuple (nama, harga)
self.left_frame = tk.Frame(self) self.images = [] # Simpan reference gambar agar tidak dihapus GC
self.left_frame.pack(side="left", fill="both", expand=True)
# Header Kiri # --- Judul Halaman ---
header = tk.Frame(self.left_frame, bg="#2c3e50", height=50) tk.Label(self.frame, text="MENU PEMBELI", font=("Arial", 18, "bold")).pack(pady=10)
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 # --- Tombol Logout ---
self.canvas = tk.Canvas(self.left_frame) tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
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"))) # --- Frame Menu ---
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.menu_frame = tk.Frame(self.frame)
self.canvas.configure(yscrollcommand=self.scrollbar.set) self.menu_frame.pack(pady=10)
self.canvas.pack(side="left", fill="both", expand=True) self.load_menu() # Load menu dari database
self.scrollbar.pack(side="right", fill="y")
# 2. Frame Kanan (Keranjang Belanja) # --- Daftar Pesanan ---
self.right_frame = tk.Frame(self, bg="#ecf0f1", width=300) tk.Label(self.frame, text="Daftar Pesanan:", font=("Arial", 12, "bold")).pack(pady=5)
self.right_frame.pack(side="right", fill="y") self.listbox = tk.Listbox(self.frame, width=50, height=6)
self.right_frame.pack_propagate(False) # Agar lebar tetap 300px self.listbox.pack()
tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x") # --- Total ---
self.total_lbl = tk.Label(self.frame, text="Total: Rp 0", font=("Arial", 12, "bold"))
self.total_lbl.pack(pady=5)
# Listbox Keranjang # --- Tombol Checkout ---
self.cart_listbox = tk.Listbox(self.right_frame, font=("Arial", 10)) tk.Button(self.frame, text="Checkout", bg="#d1e7dd", command=self.checkout).pack(pady=5)
self.cart_listbox.pack(fill="both", expand=True, padx=10, pady=10)
# 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)
# 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)
def update_data(self):
"""Dipanggil saat halaman dibuka"""
self.load_menu()
self.keranjang = [] # Reset keranjang tiap login baru
self.update_keranjang_ui()
def load_menu(self): def load_menu(self):
# Bersihkan area menu
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
self.image_refs.clear()
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT * FROM menu") cur.execute("SELECT nama, harga, gambar FROM menu")
items = cur.fetchall() data = cur.fetchall()
db.close() db.close()
columns = 3 for i, (nama, harga, gambar) in enumerate(data):
for index, item in enumerate(items): f = tk.Frame(self.menu_frame, bd=2, relief="ridge")
self.create_card(item, index, columns) f.grid(row=i//3, column=i%3, padx=10, pady=10)
def create_card(self, item, index, columns):
id_menu, nama, kategori, harga, stok, file_gambar = item
row = index // columns
col = index % columns
card = tk.Frame(self.scrollable_frame, bd=2, relief="groove", bg="white")
card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
# Load Gambar
path = os.path.join("aset", file_gambar)
try: try:
img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS) img = Image.open(gambar).resize((120, 90))
photo = ImageTk.PhotoImage(img) photo = ImageTk.PhotoImage(img)
self.image_refs.append(photo) self.images.append(photo)
tk.Label(card, image=photo, bg="white").pack(pady=5) tk.Label(f, image=photo).pack()
except: except FileNotFoundError:
tk.Label(card, text="[No Image]", bg="#eee", height=4).pack(pady=5) tk.Label(f, text="No Image").pack()
tk.Label(card, text=nama, font=("Arial", 10, "bold"), bg="white").pack() tk.Label(f, text=nama).pack()
tk.Label(card, text=f"Rp {int(harga):,}", fg="green", bg="white").pack() tk.Label(f, text=f"Rp {harga:,}").pack()
tk.Button(f, text="Pesan", bg="#cfe2ff",
command=lambda n=nama, h=harga: self.add(n, h)).pack(pady=3)
# Tombol Tambah def add(self, nama, harga):
state = "normal" if stok > 0 else "disabled" self.cart.append((nama, harga))
text_btn = "Tambah" if stok > 0 else "Habis" self.listbox.insert(tk.END, f"{nama} - Rp {harga:,}")
tk.Button(card, text=text_btn, bg="#3498db", fg="white", state=state, total = sum(h for _, h in self.cart)
command=lambda: self.tambah_ke_keranjang(item)).pack(pady=5, padx=5, fill="x") self.total_lbl.config(text=f"Total: Rp {total:,}")
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
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()
def checkout(self): def checkout(self):
if not self.keranjang: if not self.cart:
messagebox.showwarning("Kosong", "Keranjang masih kosong!") messagebox.showwarning("Pesan Kosong", "Belum ada pesanan!")
return return
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()
for nama, harga in self.cart:
try: cur.execute("INSERT INTO orders VALUES (NULL, ?, ?)", (nama, harga))
# 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() db.commit()
messagebox.showinfo("Berhasil", "Pesanan berhasil dibuat! Silakan bayar di kasir.")
# Reset
self.keranjang = []
self.update_keranjang_ui()
self.load_menu() # Reload menu untuk update stok visual
except Exception as e:
db.rollback()
messagebox.showerror("Error", f"Gagal Checkout: {e}")
finally:
db.close() db.close()
messagebox.showinfo("Sukses", "Pesanan dikirim ke kasir")
self.cart.clear()
self.listbox.delete(0, tk.END)
self.total_lbl.config(text="Total: Rp 0")
def logout(self):
self.frame.destroy()
from main import LoginScreen
LoginScreen(self.parent)

View File

@ -1,43 +1,38 @@
import tkinter as tk import tkinter as tk
from database import connect from database import connect
class PemilikPage(tk.Frame): class PemilikPage:
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) self.parent = parent
self.controller = controller self.controller = controller
self.frame = tk.Frame(parent)
self.frame.pack(fill="both", expand=True)
# --- PERBAIKAN: Baris self.pack() sudah DIHAPUS --- tk.Label(self.frame, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=10)
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
# Judul Halaman # Label untuk total penjualan
tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20) self.total_lbl = tk.Label(self.frame, text="Total Penjualan: Rp 0", font=("Arial", 14, "bold"))
self.total_lbl.pack(pady=5)
# Label Total # Tombol Refresh
self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green") tk.Button(self.frame, text="Refresh", bg="#cfe2ff", command=self.load).pack(pady=5)
self.total_lbl.pack(pady=20)
tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack() # Tombol Logout
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
# Tombol-tombol # Load data awal
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):
"""Dipanggil otomatis saat halaman dibuka"""
self.load() self.load()
def load(self): def load(self):
"""Ambil data total penjualan dari database"""
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Jumlahkan semua total pembayaran dari tabel pembayaran
# Hitung sum total dari transaksi yang sudah Paid (Lunas) cur.execute("SELECT SUM(total) FROM pembayaran")
cur.execute("SELECT SUM(total) FROM transaksi WHERE status='Paid'") total = cur.fetchone()[0] or 0
result = cur.fetchone()[0]
# Kalau belum ada penjualan, set 0
total = result if result else 0
db.close() db.close()
# Update tampilan self.total_lbl.config(text=f"Total Penjualan: Rp {total:,}")
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}")
def logout(self):
self.frame.destroy()
self.controller.show_frame("Login")

View File

@ -1,60 +1,10 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox
from database import connect
class WaiterPage(tk.Frame): class WaiterDashboard(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) super().__init__(parent)
self.controller = controller
# Header tk.Label(self, text="WAITER", font=("Arial", 20, "bold")).pack(pady=20)
top = tk.Frame(self, bg="#FF9800") tk.Label(self, text="(Input pesanan manual)").pack()
top.pack(fill="x")
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.Button(self, text="Logout", command=lambda: controller.show_frame("Login")).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, font=("Arial", 10))
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="✅ Selesai Diantar (Served)", bg="#81C784", command=self.mark_served).pack(side="left", padx=5)
def update_data(self):
self.list_orders.delete(0, tk.END)
self.data_orders = []
db = connect()
cur = db.cursor()
# 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])}")
def mark_served(self):
idx = self.list_orders.curselection()
if not idx:
messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
return
selected = self.data_orders[idx[0]]
transaksi_id = selected[0]
db = connect()
cur = db.cursor()
# Update Status Transaksi jadi Served
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (transaksi_id,))
db.commit()
db.close()
messagebox.showinfo("Sukses", "Pesanan Meja selesai dilayani!")
self.update_data()